diff options
Diffstat (limited to 'BaseTools/Source/Python/build')
| -rw-r--r-- | BaseTools/Source/Python/build/BuildReport.py | 4914 | ||||
| -rw-r--r-- | BaseTools/Source/Python/build/__init__.py | 18 | ||||
| -rwxr-xr-x | BaseTools/Source/Python/build/build.py | 5578 | ||||
| -rw-r--r-- | BaseTools/Source/Python/build/buildoptions.py | 212 |
4 files changed, 5361 insertions, 5361 deletions
diff --git a/BaseTools/Source/Python/build/BuildReport.py b/BaseTools/Source/Python/build/BuildReport.py index 26dfe53fff..b550124582 100644 --- a/BaseTools/Source/Python/build/BuildReport.py +++ b/BaseTools/Source/Python/build/BuildReport.py @@ -1,2457 +1,2457 @@ -## @file
-# Routines for generating build report.
-#
-# This module contains the functionality to generate build report after
-# build all target completes successfully.
-#
-# Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
-# SPDX-License-Identifier: BSD-2-Clause-Patent
-#
-
-## Import Modules
-#
-import json
-from pathlib import Path
-import Common.LongFilePathOs as os
-import re
-import platform
-import textwrap
-import traceback
-import sys
-import time
-import struct
-import hashlib
-import subprocess
-import threading
-from datetime import datetime
-from io import BytesIO
-from Common import EdkLogger
-from Common.Misc import SaveFileOnChange
-from Common.Misc import GuidStructureByteArrayToGuidString
-from Common.Misc import GuidStructureStringToGuidString
-from Common.BuildToolError import FILE_WRITE_FAILURE
-from Common.BuildToolError import CODE_ERROR
-from Common.BuildToolError import COMMAND_FAILURE
-from Common.BuildToolError import FORMAT_INVALID
-from Common.LongFilePathSupport import OpenLongFilePath as open
-from Common.MultipleWorkspace import MultipleWorkspace as mws
-import Common.GlobalData as GlobalData
-from AutoGen.ModuleAutoGen import ModuleAutoGen
-from Common.Misc import PathClass
-from Common.StringUtils import NormPath
-from Common.DataType import *
-import collections
-from Common.Expression import *
-from GenFds.AprioriSection import DXE_APRIORI_GUID, PEI_APRIORI_GUID
-from AutoGen.IncludesAutoGen import IncludesAutoGen
-
-## 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*Type=\w+\)\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 MaxLength of line in report
-gLineMaxLength = 120
-
-## Tags for end of line in report
-gEndOfLine = "\r\n"
-
-## Tags for section start, end and separator
-gSectionStart = ">" + "=" * (gLineMaxLength - 2) + "<"
-gSectionEnd = "<" + "=" * (gLineMaxLength - 2) + ">" + "\n"
-gSectionSep = "=" * gLineMaxLength
-
-## Tags for subsection start, end and separator
-gSubSectionStart = ">" + "-" * (gLineMaxLength - 2) + "<"
-gSubSectionEnd = "<" + "-" * (gLineMaxLength - 2) + ">"
-gSubSectionSep = "-" * gLineMaxLength
-
-
-## The look up table to map PCD type to pair of report display type and DEC type
-gPcdTypeMap = {
- TAB_PCDS_FIXED_AT_BUILD : ('FIXED', TAB_PCDS_FIXED_AT_BUILD),
- TAB_PCDS_PATCHABLE_IN_MODULE: ('PATCH', TAB_PCDS_PATCHABLE_IN_MODULE),
- TAB_PCDS_FEATURE_FLAG : ('FLAG', TAB_PCDS_FEATURE_FLAG),
- TAB_PCDS_DYNAMIC : ('DYN', TAB_PCDS_DYNAMIC),
- TAB_PCDS_DYNAMIC_HII : ('DYNHII', TAB_PCDS_DYNAMIC),
- TAB_PCDS_DYNAMIC_VPD : ('DYNVPD', TAB_PCDS_DYNAMIC),
- TAB_PCDS_DYNAMIC_EX : ('DEX', TAB_PCDS_DYNAMIC_EX),
- TAB_PCDS_DYNAMIC_EX_HII : ('DEXHII', TAB_PCDS_DYNAMIC_EX),
- TAB_PCDS_DYNAMIC_EX_VPD : ('DEXVPD', TAB_PCDS_DYNAMIC_EX),
- }
-
-## The look up table to map module type to driver type
-gDriverTypeMap = {
- SUP_MODULE_SEC : '0x3 (SECURITY_CORE)',
- SUP_MODULE_PEI_CORE : '0x4 (PEI_CORE)',
- SUP_MODULE_PEIM : '0x6 (PEIM)',
- SUP_MODULE_DXE_CORE : '0x5 (DXE_CORE)',
- SUP_MODULE_DXE_DRIVER : '0x7 (DRIVER)',
- SUP_MODULE_DXE_SAL_DRIVER : '0x7 (DRIVER)',
- SUP_MODULE_DXE_SMM_DRIVER : '0x7 (DRIVER)',
- SUP_MODULE_DXE_RUNTIME_DRIVER: '0x7 (DRIVER)',
- SUP_MODULE_UEFI_DRIVER : '0x7 (DRIVER)',
- SUP_MODULE_UEFI_APPLICATION : '0x9 (APPLICATION)',
- SUP_MODULE_SMM_CORE : '0xD (SMM_CORE)',
- 'SMM_DRIVER' : '0xA (SMM)', # Extension of module type to support PI 1.1 SMM drivers
- SUP_MODULE_MM_STANDALONE : '0xE (MM_STANDALONE)',
- SUP_MODULE_MM_CORE_STANDALONE : '0xF (MM_CORE_STANDALONE)'
- }
-
-## The look up table of the supported opcode in the dependency expression binaries
-gOpCodeList = ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR"]
-
-## Save VPD Pcd
-VPDPcdList = []
-
-##
-# 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.append(String + gEndOfLine)
-
-def ByteArrayForamt(Value):
- IsByteArray = False
- SplitNum = 16
- ArrayList = []
- if Value.startswith('{') and Value.endswith('}') and not Value.startswith("{CODE("):
- Value = Value[1:-1]
- ValueList = Value.split(',')
- if len(ValueList) >= SplitNum:
- IsByteArray = True
- if IsByteArray:
- if ValueList:
- Len = len(ValueList)/SplitNum
- for i, element in enumerate(ValueList):
- ValueList[i] = '0x%02X' % int(element.strip(), 16)
- if Len:
- Id = 0
- while (Id <= Len):
- End = min(SplitNum*(Id+1), len(ValueList))
- Str = ','.join(ValueList[SplitNum*Id : End])
- if End == len(ValueList):
- Str += '}'
- ArrayList.append(Str)
- break
- else:
- Str += ','
- ArrayList.append(Str)
- Id += 1
- else:
- ArrayList = [Value + '}']
- return IsByteArray, ArrayList
-
-##
-# 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 TAB_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
-
-## Split each lines in file
-#
-# This method is used to split the lines in file to make the length of each line
-# less than MaxLength.
-#
-# @param Content The content of file
-# @param MaxLength The Max Length of the line
-#
-def FileLinesSplit(Content=None, MaxLength=None):
- ContentList = Content.split(TAB_LINE_BREAK)
- NewContent = ''
- NewContentList = []
- for Line in ContentList:
- while len(Line.rstrip()) > MaxLength:
- LineSpaceIndex = Line.rfind(TAB_SPACE_SPLIT, 0, MaxLength)
- LineSlashIndex = Line.rfind(TAB_SLASH, 0, MaxLength)
- LineBackSlashIndex = Line.rfind(TAB_BACK_SLASH, 0, MaxLength)
- if max(LineSpaceIndex, LineSlashIndex, LineBackSlashIndex) > 0:
- LineBreakIndex = max(LineSpaceIndex, LineSlashIndex, LineBackSlashIndex)
- else:
- LineBreakIndex = MaxLength
- NewContentList.append(Line[:LineBreakIndex])
- Line = Line[LineBreakIndex:]
- if Line:
- NewContentList.append(Line)
- for NewLine in NewContentList:
- NewContent += NewLine + TAB_LINE_BREAK
-
- NewContent = NewContent.replace(gEndOfLine, TAB_LINE_BREAK).replace('\r\r\n', gEndOfLine)
- return NewContent
-
-
-
-##
-# Parse binary dependency expression section
-#
-# This utility class parses the dependency expression section and translate the readable
-# GUID name and value.
-#
-class DepexParser(object):
- ##
- # Constructor function for class DepexParser
- #
- # This constructor function collect GUID values so that the readable
- # GUID name can be translated.
- #
- # @param self The object pointer
- # @param Wa Workspace context information
- #
- def __init__(self, Wa):
- self._GuidDb = {}
- for Pa in Wa.AutoGenObjectList:
- for Package in Pa.PackageList:
- for Protocol in Package.Protocols:
- GuidValue = GuidStructureStringToGuidString(Package.Protocols[Protocol])
- self._GuidDb[GuidValue.upper()] = Protocol
- for Ppi in Package.Ppis:
- GuidValue = GuidStructureStringToGuidString(Package.Ppis[Ppi])
- self._GuidDb[GuidValue.upper()] = Ppi
- for Guid in Package.Guids:
- GuidValue = GuidStructureStringToGuidString(Package.Guids[Guid])
- self._GuidDb[GuidValue.upper()] = Guid
- for Ma in Pa.ModuleAutoGenList:
- for Pcd in Ma.FixedVoidTypePcds:
- PcdValue = Ma.FixedVoidTypePcds[Pcd]
- if len(PcdValue.split(',')) == 16:
- GuidValue = GuidStructureByteArrayToGuidString(PcdValue)
- self._GuidDb[GuidValue.upper()] = Pcd
- ##
- # Parse the binary dependency expression files.
- #
- # This function parses the binary dependency expression file and translate it
- # to the instruction list.
- #
- # @param self The object pointer
- # @param DepexFileName The file name of binary dependency expression file.
- #
- def ParseDepexFile(self, DepexFileName):
- DepexFile = open(DepexFileName, "rb")
- DepexStatement = []
- OpCode = DepexFile.read(1)
- while OpCode:
- Statement = gOpCodeList[struct.unpack("B", OpCode)[0]]
- if Statement in ["BEFORE", "AFTER", "PUSH"]:
- GuidValue = "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X" % \
- struct.unpack(PACK_PATTERN_GUID, DepexFile.read(16))
- GuidString = self._GuidDb.get(GuidValue, GuidValue)
- Statement = "%s %s" % (Statement, GuidString)
- DepexStatement.append(Statement)
- OpCode = DepexFile.read(1)
-
- return DepexStatement
-
-##
-# 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 = []
-
- 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]
- for LibAutoGen in M.LibraryAutoGenList:
- if LibInfPath == LibAutoGen.MetaFile.Path:
- LibTime = LibAutoGen.BuildTime
- break
- self.LibraryList.append((LibInfPath, LibClassList, LibConstructorList, LibDesstructorList, LibDepexList, LibTime))
-
- ##
- # 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):
- if len(self.LibraryList) > 0:
- FileWrite(File, gSubSectionStart)
- FileWrite(File, TAB_BRG_LIBRARY)
- FileWrite(File, gSubSectionSep)
- for LibraryItem in self.LibraryList:
- LibInfPath = LibraryItem[0]
- FileWrite(File, LibInfPath)
-
- LibClass = LibraryItem[1]
- EdkIILibInfo = ""
- LibConstructor = " ".join(LibraryItem[2])
- if LibConstructor:
- EdkIILibInfo += " C = " + LibConstructor
- LibDestructor = " ".join(LibraryItem[3])
- if LibDestructor:
- EdkIILibInfo += " D = " + LibDestructor
- LibDepex = " ".join(LibraryItem[4])
- if LibDepex:
- EdkIILibInfo += " Depex = " + LibDepex
- if LibraryItem[5]:
- EdkIILibInfo += " Time = " + LibraryItem[5]
- 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 = ""
- self._DepexFileName = os.path.join(M.BuildDir, "OUTPUT", M.Module.BaseName + ".depex")
- ModuleType = M.ModuleType
- if not ModuleType:
- ModuleType = COMPONENT_TO_MODULE_MAP_DICT.get(M.ComponentType, "")
-
- if ModuleType in [SUP_MODULE_SEC, SUP_MODULE_PEI_CORE, SUP_MODULE_DXE_CORE, SUP_MODULE_SMM_CORE, SUP_MODULE_MM_CORE_STANDALONE, SUP_MODULE_UEFI_APPLICATION]:
- 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.DepexExpressionDict.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
- # @param GlobalDepexParser The platform global Dependency expression parser object
- #
- def GenerateReport(self, File, GlobalDepexParser):
- if not self.Depex:
- return
- FileWrite(File, gSubSectionStart)
- if os.path.isfile(self._DepexFileName):
- try:
- DepexStatements = GlobalDepexParser.ParseDepexFile(self._DepexFileName)
- FileWrite(File, "Final Dependency Expression (DEPEX) Instructions")
- for DepexStatement in DepexStatements:
- FileWrite(File, " %s" % DepexStatement)
- FileWrite(File, gSubSectionSep)
- except:
- EdkLogger.warn(None, "Dependency expression file is corrupted", self._DepexFileName)
-
- FileWrite(File, "Dependency Expression (DEPEX) from %s" % self.Source)
-
- if self.Source == "INF":
- FileWrite(File, 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, 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.ModuleArch = M.Arch
- self.FileGuid = M.Guid
- self.Size = 0
- self.BuildTimeStamp = None
- self.Hash = 0
- self.DriverType = ""
- if not M.IsLibrary:
- ModuleType = M.ModuleType
- if not ModuleType:
- ModuleType = COMPONENT_TO_MODULE_MAP_DICT.get(M.ComponentType, "")
- #
- # If a module complies to PI 1.1, promote Module type to "SMM_DRIVER"
- #
- if ModuleType == SUP_MODULE_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, "0x2 (FREE_FORM)")
- 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.BuildTime = M.BuildTime
-
- 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 report object
- # @param GlobalPredictionReport The platform global Prediction report object
- # @param GlobalDepexParser The platform global Dependency expression parser object
- # @param ReportType The kind of report items in the final report file
- #
- def GenerateReport(self, File, GlobalPcdReport, GlobalPredictionReport, GlobalDepexParser, ReportType):
- FileWrite(File, gSectionStart)
-
- FwReportFileName = os.path.join(self._BuildDir, "OUTPUT", 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.utcfromtimestamp(int(Match.group(1)))
- except IOError:
- EdkLogger.warn(None, "Fail to read report file", FwReportFileName)
-
- if "HASH" in ReportType:
- OutputDir = os.path.join(self._BuildDir, "OUTPUT")
- DefaultEFIfile = os.path.join(OutputDir, self.ModuleName + ".efi")
- if os.path.isfile(DefaultEFIfile):
- Tempfile = os.path.join(OutputDir, self.ModuleName + "_hash.tmp")
- # rebase the efi image since its base address may not zero
- cmd = ["GenFw", "--rebase", str(0), "-o", Tempfile, DefaultEFIfile]
- try:
- PopenObject = subprocess.Popen(' '.join(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
- except Exception as X:
- EdkLogger.error("GenFw", COMMAND_FAILURE, ExtraData="%s: %s" % (str(X), cmd[0]))
- EndOfProcedure = threading.Event()
- EndOfProcedure.clear()
- if PopenObject.stderr:
- StdErrThread = threading.Thread(target=ReadMessage, args=(PopenObject.stderr, EdkLogger.quiet, EndOfProcedure))
- StdErrThread.setName("STDERR-Redirector")
- StdErrThread.setDaemon(False)
- StdErrThread.start()
- # waiting for program exit
- PopenObject.wait()
- if PopenObject.stderr:
- StdErrThread.join()
- if PopenObject.returncode != 0:
- EdkLogger.error("GenFw", COMMAND_FAILURE, "Failed to generate firmware hash image for %s" % (DefaultEFIfile))
- if os.path.isfile(Tempfile):
- self.Hash = hashlib.sha1()
- buf = open(Tempfile, 'rb').read()
- if self.Hash.update(buf):
- self.Hash = self.Hash.update(buf)
- self.Hash = self.Hash.hexdigest()
- os.remove(Tempfile)
-
- FileWrite(File, "Module Summary")
- FileWrite(File, "Module Name: %s" % self.ModuleName)
- FileWrite(File, "Module Arch: %s" % self.ModuleArch)
- 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.Hash:
- FileWrite(File, "SHA1 HASH: %s *%s" % (self.Hash, self.ModuleName + ".efi"))
- if self.BuildTimeStamp:
- FileWrite(File, "Build Time Stamp: %s" % self.BuildTimeStamp)
- if self.BuildTime:
- FileWrite(File, "Module Build Time: %s" % self.BuildTime)
- 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,self.FileGuid)
-
- if "LIBRARY" in ReportType:
- self.LibraryReport.GenerateReport(File)
-
- if "DEPEX" in ReportType:
- self.DepexReport.GenerateReport(File, GlobalDepexParser)
-
- 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)
-
-def ReadMessage(From, To, ExitFlag):
- while True:
- # read one line a time
- Line = From.readline()
- # empty string means "end"
- if Line is not None and Line != b"":
- To(Line.rstrip().decode(encoding='utf-8', errors='ignore'))
- else:
- break
- if ExitFlag.isSet():
- break
-
-##
-# 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.UnusedPcds = {}
- self.ConditionalPcds = {}
- self.MaxLen = 0
- self.Arch = None
- if Wa.FdfProfile:
- self.FdfPcdSet = Wa.FdfProfile.PcdDict
- else:
- self.FdfPcdSet = {}
-
- self.DefaultStoreSingle = True
- self.SkuSingle = True
- if GlobalData.gDefaultStores and len(GlobalData.gDefaultStores) > 1:
- self.DefaultStoreSingle = False
- if GlobalData.gSkuids and len(GlobalData.gSkuids) > 1:
- self.SkuSingle = False
-
- self.ModulePcdOverride = {}
- for Pa in Wa.AutoGenObjectList:
- self.Arch = Pa.Arch
- #
- # 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)
- #
- # Collect the PCD defined in DSC/FDF file, but not used in module
- #
- UnusedPcdFullList = []
- StructPcdDict = GlobalData.gStructurePcd.get(self.Arch, collections.OrderedDict())
- for Name, Guid in StructPcdDict:
- if (Name, Guid) not in Pa.Platform.Pcds:
- Pcd = StructPcdDict[(Name, Guid)]
- PcdList = self.AllPcds.setdefault(Guid, {}).setdefault(Pcd.Type, [])
- if Pcd not in PcdList and Pcd not in UnusedPcdFullList:
- UnusedPcdFullList.append(Pcd)
- for item in Pa.Platform.Pcds:
- Pcd = Pa.Platform.Pcds[item]
- if not Pcd.Type:
- # check the Pcd in FDF file, whether it is used in module first
- for T in PCD_TYPE_LIST:
- PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(T, [])
- if Pcd in PcdList:
- Pcd.Type = T
- break
- if not Pcd.Type:
- PcdTypeFlag = False
- for package in Pa.PackageList:
- for T in PCD_TYPE_LIST:
- if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, T) in package.Pcds:
- Pcd.Type = T
- PcdTypeFlag = True
- if not Pcd.DatumType:
- Pcd.DatumType = package.Pcds[(Pcd.TokenCName, Pcd.TokenSpaceGuidCName, T)].DatumType
- break
- if PcdTypeFlag:
- break
- if not Pcd.DatumType:
- PcdType = Pcd.Type
- # Try to remove Hii and Vpd suffix
- if PcdType.startswith(TAB_PCDS_DYNAMIC_EX):
- PcdType = TAB_PCDS_DYNAMIC_EX
- elif PcdType.startswith(TAB_PCDS_DYNAMIC):
- PcdType = TAB_PCDS_DYNAMIC
- for package in Pa.PackageList:
- if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, PcdType) in package.Pcds:
- Pcd.DatumType = package.Pcds[(Pcd.TokenCName, Pcd.TokenSpaceGuidCName, PcdType)].DatumType
- break
-
- PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
- UnusedPcdList = self.UnusedPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
- if Pcd in UnusedPcdList:
- UnusedPcdList.remove(Pcd)
- if Pcd not in PcdList and Pcd not in UnusedPcdFullList:
- UnusedPcdFullList.append(Pcd)
- if len(Pcd.TokenCName) > self.MaxLen:
- self.MaxLen = len(Pcd.TokenCName)
-
- if GlobalData.gConditionalPcds:
- for PcdItem in GlobalData.gConditionalPcds:
- if '.' in PcdItem:
- (TokenSpaceGuidCName, TokenCName) = PcdItem.split('.')
- if (TokenCName, TokenSpaceGuidCName) in Pa.Platform.Pcds:
- Pcd = Pa.Platform.Pcds[(TokenCName, TokenSpaceGuidCName)]
- PcdList = self.ConditionalPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
- if Pcd not in PcdList:
- PcdList.append(Pcd)
-
- UnusedPcdList = []
- if UnusedPcdFullList:
- for Pcd in UnusedPcdFullList:
- if Pcd.TokenSpaceGuidCName + '.' + Pcd.TokenCName in GlobalData.gConditionalPcds:
- continue
- UnusedPcdList.append(Pcd)
-
- for Pcd in UnusedPcdList:
- PcdList = self.UnusedPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
- if Pcd not in PcdList:
- PcdList.append(Pcd)
-
- 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 = {}
- self._GuidDict = {}
- for Pa in Wa.AutoGenObjectList:
- for Package in Pa.PackageList:
- Guids = Package.Guids
- self._GuidDict.update(Guids)
- 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 Pa in Wa.AutoGenObjectList:
- for (TokenCName, TokenSpaceGuidCName) in Pa.Platform.Pcds:
- DscDefaultValue = Pa.Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DscDefaultValue
- if DscDefaultValue:
- self.DscPcdDefault[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
-
- def GenerateReport(self, File, ModulePcdSet,ModuleGuid=None):
- if not ModulePcdSet:
- if self.ConditionalPcds:
- self.GenerateReportDetail(File, ModulePcdSet, 1)
- if self.UnusedPcds:
- IsEmpty = True
- for Token in self.UnusedPcds:
- TokenDict = self.UnusedPcds[Token]
- for Type in TokenDict:
- if TokenDict[Type]:
- IsEmpty = False
- break
- if not IsEmpty:
- break
- if not IsEmpty:
- self.GenerateReportDetail(File, ModulePcdSet, 2)
- self.GenerateReportDetail(File, ModulePcdSet,ModuleGuid = ModuleGuid)
-
- ##
- # 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 ReportySubType 0 means platform/module PCD report, 1 means Conditional
- # directives section report, 2 means Unused Pcds section report
- # @param DscOverridePcds Module DSC override PCDs set
- #
- def GenerateReportDetail(self, File, ModulePcdSet, ReportSubType = 0,ModuleGuid=None):
- PcdDict = self.AllPcds
- if ReportSubType == 1:
- PcdDict = self.ConditionalPcds
- elif ReportSubType == 2:
- PcdDict = self.UnusedPcds
-
- if not ModulePcdSet:
- FileWrite(File, gSectionStart)
- if ReportSubType == 1:
- FileWrite(File, "Conditional Directives used by the build system")
- elif ReportSubType == 2:
- FileWrite(File, "PCDs not used by modules or in conditional directives")
- else:
- FileWrite(File, "Platform Configuration Database Report")
-
- FileWrite(File, " *B - PCD override in the build option")
- FileWrite(File, " *P - Platform scoped PCD override in DSC file")
- FileWrite(File, " *F - Platform scoped PCD override in FDF file")
- if not ReportSubType:
- FileWrite(File, " *M - Module scoped PCD override")
- FileWrite(File, gSectionSep)
- else:
- if not ReportSubType and ModulePcdSet:
- #
- # For module PCD sub-section
- #
- FileWrite(File, gSubSectionStart)
- FileWrite(File, TAB_BRG_PCD)
- FileWrite(File, gSubSectionSep)
- AllPcdDict = {}
- for Key in PcdDict:
- AllPcdDict[Key] = {}
- for Type in PcdDict[Key]:
- for Pcd in PcdDict[Key][Type]:
- AllPcdDict[Key][(Pcd.TokenCName, Type)] = Pcd
- for Key in sorted(AllPcdDict):
- #
- # Group PCD by their token space GUID C Name
- #
- First = True
- for PcdTokenCName, Type in sorted(AllPcdDict[Key]):
- #
- # Group PCD by their usage type
- #
- Pcd = AllPcdDict[Key][(PcdTokenCName, Type)]
- TypeName, DecType = gPcdTypeMap.get(Type, ("", Type))
- MixedPcdFlag = False
- if GlobalData.MixedPcd:
- for PcdKey in GlobalData.MixedPcd:
- if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName) in GlobalData.MixedPcd[PcdKey]:
- PcdTokenCName = PcdKey[0]
- MixedPcdFlag = True
- if MixedPcdFlag and not ModulePcdSet:
- continue
- #
- # 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))
- DscDefaultValBak = DscDefaultValue
- Field = ''
- for (CName, Guid, Field) in self.FdfPcdSet:
- if CName == PcdTokenCName and Guid == Key:
- DscDefaultValue = self.FdfPcdSet[(CName, Guid, Field)]
- break
- if DscDefaultValue != DscDefaultValBak:
- try:
- DscDefaultValue = ValueExpressionEx(DscDefaultValue, Pcd.DatumType, self._GuidDict)(True)
- except BadExpression as DscDefaultValue:
- EdkLogger.error('BuildReport', FORMAT_INVALID, "PCD Value: %s, Type: %s" %(DscDefaultValue, Pcd.DatumType))
-
- InfDefaultValue = None
-
- PcdValue = DecDefaultValue
- if DscDefaultValue:
- PcdValue = DscDefaultValue
- #The DefaultValue of StructurePcd already be the latest, no need to update.
- if not self.IsStructurePcd(Pcd.TokenCName, Pcd.TokenSpaceGuidCName):
- Pcd.DefaultValue = PcdValue
- PcdComponentValue = None
- if ModulePcdSet is not None:
- if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type) not in ModulePcdSet:
- continue
- InfDefaultValue, PcdComponentValue = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type]
- PcdValue = PcdComponentValue
- #The DefaultValue of StructurePcd already be the latest, no need to update.
- if not self.IsStructurePcd(Pcd.TokenCName, Pcd.TokenSpaceGuidCName):
- Pcd.DefaultValue = PcdValue
- if InfDefaultValue:
- try:
- InfDefaultValue = ValueExpressionEx(InfDefaultValue, Pcd.DatumType, self._GuidDict)(True)
- except BadExpression as InfDefaultValue:
- EdkLogger.error('BuildReport', FORMAT_INVALID, "PCD Value: %s, Type: %s" % (InfDefaultValue, Pcd.DatumType))
- if InfDefaultValue == "":
- InfDefaultValue = None
-
- BuildOptionMatch = False
- if GlobalData.BuildOptionPcd:
- for pcd in GlobalData.BuildOptionPcd:
- if (Pcd.TokenSpaceGuidCName, Pcd.TokenCName) == (pcd[0], pcd[1]):
- if pcd[2]:
- continue
- PcdValue = pcd[3]
- #The DefaultValue of StructurePcd already be the latest, no need to update.
- if not self.IsStructurePcd(Pcd.TokenCName, Pcd.TokenSpaceGuidCName):
- Pcd.DefaultValue = PcdValue
- BuildOptionMatch = True
- break
-
- if First:
- if ModulePcdSet is None:
- FileWrite(File, "")
- FileWrite(File, Key)
- First = False
-
-
- if Pcd.DatumType in TAB_PCD_NUMERIC_TYPES:
- if PcdValue.startswith('0') and not PcdValue.lower().startswith('0x') and \
- len(PcdValue) > 1 and PcdValue.lstrip('0'):
- PcdValue = PcdValue.lstrip('0')
- PcdValueNumber = int(PcdValue.strip(), 0)
- if DecDefaultValue is None:
- DecMatch = True
- else:
- if DecDefaultValue.startswith('0') and not DecDefaultValue.lower().startswith('0x') and \
- len(DecDefaultValue) > 1 and DecDefaultValue.lstrip('0'):
- DecDefaultValue = DecDefaultValue.lstrip('0')
- DecDefaultValueNumber = int(DecDefaultValue.strip(), 0)
- DecMatch = (DecDefaultValueNumber == PcdValueNumber)
-
- if InfDefaultValue is None:
- InfMatch = True
- else:
- if InfDefaultValue.startswith('0') and not InfDefaultValue.lower().startswith('0x') and \
- len(InfDefaultValue) > 1 and InfDefaultValue.lstrip('0'):
- InfDefaultValue = InfDefaultValue.lstrip('0')
- InfDefaultValueNumber = int(InfDefaultValue.strip(), 0)
- InfMatch = (InfDefaultValueNumber == PcdValueNumber)
-
- if DscDefaultValue is None:
- DscMatch = True
- else:
- if DscDefaultValue.startswith('0') and not DscDefaultValue.lower().startswith('0x') and \
- len(DscDefaultValue) > 1 and DscDefaultValue.lstrip('0'):
- DscDefaultValue = DscDefaultValue.lstrip('0')
- DscDefaultValueNumber = int(DscDefaultValue.strip(), 0)
- DscMatch = (DscDefaultValueNumber == PcdValueNumber)
- else:
- if DecDefaultValue is None:
- DecMatch = True
- else:
- DecMatch = (DecDefaultValue.strip() == PcdValue.strip())
-
- if InfDefaultValue is None:
- InfMatch = True
- else:
- InfMatch = (InfDefaultValue.strip() == PcdValue.strip())
-
- if DscDefaultValue is None:
- DscMatch = True
- else:
- DscMatch = (DscDefaultValue.strip() == PcdValue.strip())
-
- IsStructure = False
- if self.IsStructurePcd(Pcd.TokenCName, Pcd.TokenSpaceGuidCName):
- IsStructure = True
- if TypeName in ('DYNVPD', 'DEXVPD'):
- SkuInfoList = Pcd.SkuInfoList
- Pcd = GlobalData.gStructurePcd[self.Arch][(Pcd.TokenCName, Pcd.TokenSpaceGuidCName)]
- if ModulePcdSet and ModulePcdSet.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type)):
- InfDefaultValue, PcdComponentValue = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type]
- DscDefaultValBak = Pcd.DefaultValue
- Pcd.DefaultValue = PcdComponentValue
-
- Pcd.DatumType = Pcd.StructName
- if TypeName in ('DYNVPD', 'DEXVPD'):
- Pcd.SkuInfoList = SkuInfoList
- if Pcd.PcdValueFromComm or Pcd.PcdFieldValueFromComm:
- BuildOptionMatch = True
- DecMatch = False
- elif Pcd.PcdValueFromFdf or Pcd.PcdFieldValueFromFdf:
- DscDefaultValue = True
- DscMatch = True
- DecMatch = False
- else:
- if Pcd.Type in PCD_DYNAMIC_TYPE_SET | PCD_DYNAMIC_EX_TYPE_SET:
- DscOverride = False
- if Pcd.DefaultFromDSC:
- DscOverride = True
- else:
- DictLen = 0
- for item in Pcd.SkuOverrideValues:
- DictLen += len(Pcd.SkuOverrideValues[item])
- if not DictLen:
- DscOverride = False
- else:
- if not Pcd.SkuInfoList:
- OverrideValues = Pcd.SkuOverrideValues
- if OverrideValues:
- for Data in OverrideValues.values():
- Struct = list(Data.values())
- if Struct:
- DscOverride = self.ParseStruct(Struct[0])
- break
- else:
- SkuList = sorted(Pcd.SkuInfoList.keys())
- for Sku in SkuList:
- SkuInfo = Pcd.SkuInfoList[Sku]
- if SkuInfo.DefaultStoreDict:
- DefaultStoreList = sorted(SkuInfo.DefaultStoreDict.keys())
- for DefaultStore in DefaultStoreList:
- OverrideValues = Pcd.SkuOverrideValues.get(Sku)
- if OverrideValues:
- DscOverride = self.ParseStruct(OverrideValues[DefaultStore])
- if DscOverride:
- break
- if DscOverride:
- break
- if DscOverride:
- DscDefaultValue = True
- DscMatch = True
- DecMatch = False
- else:
- DecMatch = True
- else:
- if Pcd.DscRawValue or (ModuleGuid and ModuleGuid.replace("-","S") in Pcd.PcdValueFromComponents):
- DscDefaultValue = True
- DscMatch = True
- DecMatch = False
- else:
- DscDefaultValue = False
- DecMatch = True
-
- #
- # Report PCD item according to their override relationship
- #
- if Pcd.DatumType == 'BOOLEAN':
- if DscDefaultValue:
- DscDefaultValue = str(int(DscDefaultValue, 0))
- if DecDefaultValue:
- DecDefaultValue = str(int(DecDefaultValue, 0))
- if InfDefaultValue:
- InfDefaultValue = str(int(InfDefaultValue, 0))
- if Pcd.DefaultValue:
- Pcd.DefaultValue = str(int(Pcd.DefaultValue, 0))
- if DecMatch:
- self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, ' ')
- elif InfDefaultValue and InfMatch:
- self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '*M')
- elif BuildOptionMatch:
- self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '*B')
- else:
- if PcdComponentValue:
- self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, PcdComponentValue, DecMatch, DecDefaultValue, '*M', ModuleGuid)
- elif DscDefaultValue and DscMatch:
- if (Pcd.TokenCName, Key, Field) in self.FdfPcdSet:
- self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '*F')
- else:
- self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '*P')
-
-
- if ModulePcdSet is None:
- if IsStructure:
- continue
- if not TypeName in ('PATCH', 'FLAG', 'FIXED'):
- continue
- if not BuildOptionMatch:
- ModuleOverride = self.ModulePcdOverride.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName), {})
- for ModulePath in ModuleOverride:
- ModuleDefault = ModuleOverride[ModulePath]
- if Pcd.DatumType in TAB_PCD_NUMERIC_TYPES:
- if ModuleDefault.startswith('0') and not ModuleDefault.lower().startswith('0x') and \
- len(ModuleDefault) > 1 and ModuleDefault.lstrip('0'):
- ModuleDefault = ModuleDefault.lstrip('0')
- ModulePcdDefaultValueNumber = int(ModuleDefault.strip(), 0)
- Match = (ModulePcdDefaultValueNumber == PcdValueNumber)
- if Pcd.DatumType == 'BOOLEAN':
- ModuleDefault = str(ModulePcdDefaultValueNumber)
- else:
- Match = (ModuleDefault.strip() == PcdValue.strip())
- if Match:
- continue
- IsByteArray, ArrayList = ByteArrayForamt(ModuleDefault.strip())
- if IsByteArray:
- FileWrite(File, ' *M %-*s = %s' % (self.MaxLen + 15, ModulePath, '{'))
- for Array in ArrayList:
- FileWrite(File, Array)
- else:
- Value = ModuleDefault.strip()
- if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
- if Value.startswith(('0x', '0X')):
- Value = '{} ({:d})'.format(Value, int(Value, 0))
- else:
- Value = "0x{:X} ({})".format(int(Value, 0), Value)
- FileWrite(File, ' *M %-*s = %s' % (self.MaxLen + 15, ModulePath, Value))
-
- if ModulePcdSet is None:
- FileWrite(File, gSectionEnd)
- else:
- if not ReportSubType and ModulePcdSet:
- FileWrite(File, gSubSectionEnd)
-
- def ParseStruct(self, struct):
- HasDscOverride = False
- if struct:
- for _, Values in list(struct.items()):
- for Key, value in Values.items():
- if value[1] and value[1].endswith('.dsc'):
- HasDscOverride = True
- break
- if HasDscOverride == True:
- break
- return HasDscOverride
-
- def PrintPcdDefault(self, File, Pcd, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue):
- if not DscMatch and DscDefaultValue is not None:
- Value = DscDefaultValue.strip()
- IsByteArray, ArrayList = ByteArrayForamt(Value)
- if IsByteArray:
- FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', "{"))
- for Array in ArrayList:
- FileWrite(File, Array)
- else:
- if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
- if Value.startswith(('0x', '0X')):
- Value = '{} ({:d})'.format(Value, int(Value, 0))
- else:
- Value = "0x{:X} ({})".format(int(Value, 0), Value)
- FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', Value))
- if not InfMatch and InfDefaultValue is not None:
- Value = InfDefaultValue.strip()
- IsByteArray, ArrayList = ByteArrayForamt(Value)
- if IsByteArray:
- FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', "{"))
- for Array in ArrayList:
- FileWrite(File, Array)
- else:
- if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
- if Value.startswith(('0x', '0X')):
- Value = '{} ({:d})'.format(Value, int(Value, 0))
- else:
- Value = "0x{:X} ({})".format(int(Value, 0), Value)
- FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', Value))
-
- if not DecMatch and DecDefaultValue is not None:
- Value = DecDefaultValue.strip()
- IsByteArray, ArrayList = ByteArrayForamt(Value)
- if IsByteArray:
- FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', "{"))
- for Array in ArrayList:
- FileWrite(File, Array)
- else:
- if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
- if Value.startswith(('0x', '0X')):
- Value = '{} ({:d})'.format(Value, int(Value, 0))
- else:
- Value = "0x{:X} ({})".format(int(Value, 0), Value)
- FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', Value))
- if IsStructure:
- for filedvalues in Pcd.DefaultValues.values():
- self.PrintStructureInfo(File, filedvalues)
- if DecMatch and IsStructure:
- for filedvalues in Pcd.DefaultValues.values():
- self.PrintStructureInfo(File, filedvalues)
-
- def PrintPcdValue(self, File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, Flag = ' ',ModuleGuid=None):
- if not Pcd.SkuInfoList:
- Value = Pcd.DefaultValue
- IsByteArray, ArrayList = ByteArrayForamt(Value)
- if IsByteArray:
- FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '{'))
- for Array in ArrayList:
- FileWrite(File, Array)
- else:
- if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
- if Value.startswith('0') and not Value.lower().startswith('0x') and len(Value) > 1 and Value.lstrip('0'):
- Value = Value.lstrip('0')
- if Value.startswith(('0x', '0X')):
- Value = '{} ({:d})'.format(Value, int(Value, 0))
- else:
- Value = "0x{:X} ({})".format(int(Value, 0), Value)
- FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', Value))
- if IsStructure:
- FiledOverrideFlag = False
- if (Pcd.TokenCName,Pcd.TokenSpaceGuidCName) in GlobalData.gPcdSkuOverrides:
- OverrideValues = GlobalData.gPcdSkuOverrides[(Pcd.TokenCName,Pcd.TokenSpaceGuidCName)]
- else:
- OverrideValues = Pcd.SkuOverrideValues
- FieldOverrideValues = None
- if OverrideValues:
- for Data in OverrideValues.values():
- Struct = list(Data.values())
- if Struct:
- FieldOverrideValues = Struct[0]
- FiledOverrideFlag = True
- break
- if Pcd.PcdFiledValueFromDscComponent and ModuleGuid and ModuleGuid.replace("-","S") in Pcd.PcdFiledValueFromDscComponent:
- FieldOverrideValues = Pcd.PcdFiledValueFromDscComponent[ModuleGuid.replace("-","S")]
- if FieldOverrideValues:
- OverrideFieldStruct = self.OverrideFieldValue(Pcd, FieldOverrideValues)
- self.PrintStructureInfo(File, OverrideFieldStruct)
-
- if not FiledOverrideFlag and (Pcd.PcdFieldValueFromComm or Pcd.PcdFieldValueFromFdf):
- OverrideFieldStruct = self.OverrideFieldValue(Pcd, {})
- self.PrintStructureInfo(File, OverrideFieldStruct)
- self.PrintPcdDefault(File, Pcd, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue)
- else:
- FirstPrint = True
- SkuList = sorted(Pcd.SkuInfoList.keys())
- for Sku in SkuList:
- SkuInfo = Pcd.SkuInfoList[Sku]
- SkuIdName = SkuInfo.SkuIdName
- if TypeName in ('DYNHII', 'DEXHII'):
- if SkuInfo.DefaultStoreDict:
- DefaultStoreList = sorted(SkuInfo.DefaultStoreDict.keys())
- for DefaultStore in DefaultStoreList:
- Value = SkuInfo.DefaultStoreDict[DefaultStore]
- IsByteArray, ArrayList = ByteArrayForamt(Value)
- if Pcd.DatumType == 'BOOLEAN':
- Value = str(int(Value, 0))
- if FirstPrint:
- FirstPrint = False
- if IsByteArray:
- if self.DefaultStoreSingle and self.SkuSingle:
- FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '{'))
- elif self.DefaultStoreSingle and not self.SkuSingle:
- FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '{'))
- elif not self.DefaultStoreSingle and self.SkuSingle:
- FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + DefaultStore + ')', '{'))
- else:
- FileWrite(File, ' %-*s : %6s %10s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '(' + DefaultStore + ')', '{'))
- for Array in ArrayList:
- FileWrite(File, Array)
- else:
- if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
- if Value.startswith(('0x', '0X')):
- Value = '{} ({:d})'.format(Value, int(Value, 0))
- else:
- Value = "0x{:X} ({})".format(int(Value, 0), Value)
- if self.DefaultStoreSingle and self.SkuSingle:
- FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', Value))
- elif self.DefaultStoreSingle and not self.SkuSingle:
- FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', Value))
- elif not self.DefaultStoreSingle and self.SkuSingle:
- FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + DefaultStore + ')', Value))
- else:
- FileWrite(File, ' %-*s : %6s %10s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '(' + DefaultStore + ')', Value))
- else:
- if IsByteArray:
- if self.DefaultStoreSingle and self.SkuSingle:
- FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '{'))
- elif self.DefaultStoreSingle and not self.SkuSingle:
- FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '{'))
- elif not self.DefaultStoreSingle and self.SkuSingle:
- FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + DefaultStore + ')', '{'))
- else:
- FileWrite(File, ' %-*s : %6s %10s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '(' + DefaultStore + ')', '{'))
- for Array in ArrayList:
- FileWrite(File, Array)
- else:
- if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
- if Value.startswith(('0x', '0X')):
- Value = '{} ({:d})'.format(Value, int(Value, 0))
- else:
- Value = "0x{:X} ({})".format(int(Value, 0), Value)
- if self.DefaultStoreSingle and self.SkuSingle:
- FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', Value))
- elif self.DefaultStoreSingle and not self.SkuSingle:
- FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', Value))
- elif not self.DefaultStoreSingle and self.SkuSingle:
- FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + DefaultStore + ')', Value))
- else:
- FileWrite(File, ' %-*s : %6s %10s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '(' + DefaultStore + ')', Value))
- FileWrite(File, '%*s: %s: %s' % (self.MaxLen + 4, SkuInfo.VariableGuid, SkuInfo.VariableName, SkuInfo.VariableOffset))
- if IsStructure:
- OverrideValues = Pcd.SkuOverrideValues.get(Sku)
- if OverrideValues:
- OverrideFieldStruct = self.OverrideFieldValue(Pcd, OverrideValues[DefaultStore])
- self.PrintStructureInfo(File, OverrideFieldStruct)
- self.PrintPcdDefault(File, Pcd, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue)
- else:
- Value = SkuInfo.DefaultValue
- IsByteArray, ArrayList = ByteArrayForamt(Value)
- if Pcd.DatumType == 'BOOLEAN':
- Value = str(int(Value, 0))
- if FirstPrint:
- FirstPrint = False
- if IsByteArray:
- if self.SkuSingle:
- FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', "{"))
- else:
- FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', "{"))
- for Array in ArrayList:
- FileWrite(File, Array)
- else:
- if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
- if Value.startswith(('0x', '0X')):
- Value = '{} ({:d})'.format(Value, int(Value, 0))
- else:
- Value = "0x{:X} ({})".format(int(Value, 0), Value)
- if self.SkuSingle:
- FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', Value))
- else:
- FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', Value))
- else:
- if IsByteArray:
- if self.SkuSingle:
- FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', "{"))
- else:
- FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', "{"))
- for Array in ArrayList:
- FileWrite(File, Array)
- else:
- if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
- if Value.startswith(('0x', '0X')):
- Value = '{} ({:d})'.format(Value, int(Value, 0))
- else:
- Value = "0x{:X} ({})".format(int(Value, 0), Value)
- if self.SkuSingle:
- FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', Value))
- else:
- FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', Value))
- if TypeName in ('DYNVPD', 'DEXVPD'):
- FileWrite(File, '%*s' % (self.MaxLen + 4, SkuInfo.VpdOffset))
- VPDPcdItem = (Pcd.TokenSpaceGuidCName + '.' + PcdTokenCName, SkuIdName, SkuInfo.VpdOffset, Pcd.MaxDatumSize, SkuInfo.DefaultValue)
- if VPDPcdItem not in VPDPcdList:
- PcdGuidList = self.UnusedPcds.get(Pcd.TokenSpaceGuidCName)
- if PcdGuidList:
- PcdList = PcdGuidList.get(Pcd.Type)
- if not PcdList:
- VPDPcdList.append(VPDPcdItem)
- for VpdPcd in PcdList:
- if PcdTokenCName == VpdPcd.TokenCName:
- break
- else:
- VPDPcdList.append(VPDPcdItem)
- if IsStructure:
- FiledOverrideFlag = False
- OverrideValues = Pcd.SkuOverrideValues.get(Sku)
- if OverrideValues:
- Keys = list(OverrideValues.keys())
- OverrideFieldStruct = self.OverrideFieldValue(Pcd, OverrideValues[Keys[0]])
- self.PrintStructureInfo(File, OverrideFieldStruct)
- FiledOverrideFlag = True
- if not FiledOverrideFlag and (Pcd.PcdFieldValueFromComm or Pcd.PcdFieldValueFromFdf):
- OverrideFieldStruct = self.OverrideFieldValue(Pcd, {})
- self.PrintStructureInfo(File, OverrideFieldStruct)
- self.PrintPcdDefault(File, Pcd, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue)
-
- def OverrideFieldValue(self, Pcd, OverrideStruct):
- OverrideFieldStruct = collections.OrderedDict()
- if OverrideStruct:
- for _, Values in OverrideStruct.items():
- for Key,value in Values.items():
- if value[1] and value[1].endswith('.dsc'):
- OverrideFieldStruct[Key] = value
- if Pcd.PcdFieldValueFromFdf:
- for Key, Values in Pcd.PcdFieldValueFromFdf.items():
- if Key in OverrideFieldStruct and Values[0] == OverrideFieldStruct[Key][0]:
- continue
- OverrideFieldStruct[Key] = Values
- if Pcd.PcdFieldValueFromComm:
- for Key, Values in Pcd.PcdFieldValueFromComm.items():
- if Key in OverrideFieldStruct and Values[0] == OverrideFieldStruct[Key][0]:
- continue
- OverrideFieldStruct[Key] = Values
- return OverrideFieldStruct
-
- def PrintStructureInfo(self, File, Struct):
- for Key, Value in sorted(Struct.items(), key=lambda x: x[0]):
- if Value[1] and 'build command options' in Value[1]:
- FileWrite(File, ' *B %-*s = %s' % (self.MaxLen + 4, '.' + Key, Value[0]))
- elif Value[1] and Value[1].endswith('.fdf'):
- FileWrite(File, ' *F %-*s = %s' % (self.MaxLen + 4, '.' + Key, Value[0]))
- else:
- FileWrite(File, ' %-*s = %s' % (self.MaxLen + 4, '.' + Key, Value[0]))
-
- def StrtoHex(self, value):
- try:
- value = hex(int(value))
- return value
- except:
- if value.startswith("L\"") and value.endswith("\""):
- valuelist = []
- for ch in value[2:-1]:
- valuelist.append(hex(ord(ch)))
- valuelist.append('0x00')
- return valuelist
- elif value.startswith("\"") and value.endswith("\""):
- return hex(ord(value[1:-1]))
- elif value.startswith("{") and value.endswith("}"):
- valuelist = []
- if ',' not in value:
- return value[1:-1]
- for ch in value[1:-1].split(','):
- ch = ch.strip()
- if ch.startswith('0x') or ch.startswith('0X'):
- valuelist.append(ch)
- continue
- try:
- valuelist.append(hex(int(ch.strip())))
- except:
- pass
- return valuelist
- else:
- return value
-
- def IsStructurePcd(self, PcdToken, PcdTokenSpaceGuid):
- if GlobalData.gStructurePcd and (self.Arch in GlobalData.gStructurePcd) and ((PcdToken, PcdTokenSpaceGuid) in GlobalData.gStructurePcd[self.Arch]):
- return True
- else:
- return False
-
-##
-# 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:
- #
- # BASE typed modules are EFI agnostic, so we need not scan
- # their source code to find PPI/Protocol produce or consume
- # information.
- #
- if Module.ModuleType == SUP_MODULE_BASE:
- continue
- #
- # 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)
-
- RealEntryPoint = "_ModuleEntryPoint"
-
- 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 != BINARY_FILE_TYPE_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 = []
- for Item in self._SourceList:
- FileWrite(TempFile, Item)
- SaveFileOnChange(SourceList, "".join(TempFile), False)
- TempFile = []
- for Key in self._GuidMap:
- FileWrite(TempFile, "%s %s" % (Key, self._GuidMap[Key]))
- SaveFileOnChange(GuidList, "".join(TempFile), False)
-
- try:
- from Eot.EotMain import Eot
-
- #
- # Invoke EOT tool and echo its runtime performance
- #
- EotStartTime = time.time()
- Eot(CommandLineOption=False, SourceFileList=SourceList, GuidList=GuidList,
- FvFileList=' '.join(FvFileList), Dispatch=DispatchList, IsInit=True)
- EotEndTime = time.time()
- EotDuration = time.strftime("%H:%M:%S", time.gmtime(int(round(EotEndTime - EotStartTime))))
- EdkLogger.quiet("EOT run time: %s\n" % EotDuration)
-
- #
- # 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):
- FvDictKey=FvName.upper()
- if FvDictKey in Wa.FdfProfile.FvDict:
- 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
- self._WorkspaceDir = Wa.WorkspaceDir
-
- #
- # If the input FdRegion is not a firmware volume,
- # we are done.
- #
- if self.Type != BINARY_FILE_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 Pa in Wa.AutoGenObjectList:
- for Package in Pa.PackageList:
- for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
- DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
- PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue
- #
- # Collect PCDs defined in DSC file
- #
- for Pa in Wa.AutoGenObjectList:
- for (TokenCName, TokenSpaceGuidCName) in Pa.Platform.Pcds:
- DscDefaultValue = Pa.Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
- PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
-
- #
- # Add PEI and DXE a priori files GUIDs defined in PI specification.
- #
- self._GuidsDb[PEI_APRIORI_GUID] = "PEI Apriori"
- self._GuidsDb[DXE_APRIORI_GUID] = "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 = mws.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:
- FvDictKey=FvName.upper()
- if FvDictKey in Wa.FdfProfile.FvDict:
- 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 = mws.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 == BINARY_FILE_TYPE_FV:
- FvTotalSize = 0
- FvTakenSize = 0
- FvFreeSize = 0
- if FvName.upper().endswith('.FV'):
- FileExt = FvName + ".txt"
- else:
- FileExt = FvName + ".Fv.txt"
-
- if not os.path.isfile(FileExt):
- FvReportFileName = mws.join(self._WorkspaceDir, FileExt)
- if not os.path.isfile(FvReportFileName):
- FvReportFileName = os.path.join(self._FvDir, FileExt)
- 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 = sorted(OffsetInfo.keys())
- 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], TAB_FV_DIRECTORY, 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]
- self.FvPath = os.path.join(Wa.BuildDir, TAB_FV_DIRECTORY)
- self.VPDBaseAddress = 0
- self.VPDSize = 0
- for index, FdRegion in enumerate(Fd.RegionList):
- if str(FdRegion.RegionType) == 'FILE' and Wa.Platform.VpdToolGuid in str(FdRegion.RegionDataList):
- self.VPDBaseAddress = self.FdRegionList[index].BaseAddress
- self.VPDSize = self.FdRegionList[index].Size
- break
-
- ##
- # 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)
-
- if VPDPcdList:
- VPDPcdList.sort(key=lambda x: int(x[2], 0))
- FileWrite(File, gSubSectionStart)
- FileWrite(File, "FD VPD Region")
- FileWrite(File, "Base Address: 0x%X" % self.VPDBaseAddress)
- FileWrite(File, "Size: 0x%X (%.0fK)" % (self.VPDSize, self.VPDSize / 1024.0))
- FileWrite(File, gSubSectionSep)
- for item in VPDPcdList:
- # Add BaseAddress for offset
- Offset = '0x%08X' % (int(item[2], 16) + self.VPDBaseAddress)
- IsByteArray, ArrayList = ByteArrayForamt(item[-1])
- Skuinfo = item[1]
- if len(GlobalData.gSkuids) == 1 :
- Skuinfo = GlobalData.gSkuids[0]
- if IsByteArray:
- FileWrite(File, "%s | %s | %s | %s | %s" % (item[0], Skuinfo, Offset, item[3], '{'))
- for Array in ArrayList:
- FileWrite(File, Array)
- else:
- FileWrite(File, "%s | %s | %s | %s | %s" % (item[0], Skuinfo, Offset, item[3], item[-1]))
- FileWrite(File, gSubSectionEnd)
- 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
- # @param MaList The list of modules in the platform build
- #
- def __init__(self, Wa, MaList, 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 and MaList is None:
- 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.DepexParser = None
- if "DEPEX" in ReportType:
- self.DepexParser = DepexParser(Wa)
-
- self.ModuleReportList = []
- if MaList is not None:
- self._IsModuleBuild = True
- for Ma in MaList:
- self.ModuleReportList.append(ModuleReport(Ma, ReportType))
- else:
- self._IsModuleBuild = False
- for Pa in Wa.AutoGenObjectList:
- ModuleAutoGenList = []
- for ModuleKey in Pa.Platform.Modules:
- ModuleAutoGenList.append(Pa.Platform.Modules[ModuleKey].M)
- if GlobalData.gFdfParser is not None:
- if Pa.Arch in GlobalData.gFdfParser.Profile.InfDict:
- INFList = GlobalData.gFdfParser.Profile.InfDict[Pa.Arch]
- for InfName in INFList:
- InfClass = PathClass(NormPath(InfName), Wa.WorkspaceDir, Pa.Arch)
- Ma = ModuleAutoGen(Wa, InfClass, Pa.BuildTarget, Pa.ToolChain, Pa.Arch, Wa.MetaFile, Pa.DataPipe)
- if Ma is None:
- continue
- if Ma not in ModuleAutoGenList:
- ModuleAutoGenList.append(Ma)
- for MGen in ModuleAutoGenList:
- self.ModuleReportList.append(ModuleReport(MGen, 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 AutoGenTime The total time of AutoGen Phase
- # @param MakeTime The total time of Make Phase
- # @param GenFdsTime The total time of GenFds Phase
- # @param ReportType The kind of report items in the final report file
- #
- def GenerateReport(self, File, BuildDuration, AutoGenTime, MakeTime, GenFdsTime, 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)
- if GlobalData.gSkuids:
- FileWrite(File, "SKUID: %s" % " ".join(GlobalData.gSkuids))
- if GlobalData.gDefaultStores:
- FileWrite(File, "DefaultStore: %s" % " ".join(GlobalData.gDefaultStores))
- FileWrite(File, "Output Path: %s" % self.OutputPath)
- FileWrite(File, "Build Environment: %s" % self.BuildEnvironment)
- FileWrite(File, "Build Duration: %s" % BuildDuration)
- if AutoGenTime:
- FileWrite(File, "AutoGen Duration: %s" % AutoGenTime)
- if MakeTime:
- FileWrite(File, "Make Duration: %s" % MakeTime)
- if GenFdsTime:
- FileWrite(File, "GenFds Duration: %s" % GenFdsTime)
- FileWrite(File, "Report Content: %s" % ", ".join(ReportType))
-
- if GlobalData.MixedPcd:
- FileWrite(File, gSectionStart)
- FileWrite(File, "The following PCDs use different access methods:")
- FileWrite(File, gSectionSep)
- for PcdItem in GlobalData.MixedPcd:
- FileWrite(File, "%s.%s" % (str(PcdItem[1]), str(PcdItem[0])))
- FileWrite(File, gSectionEnd)
-
- if not self._IsModuleBuild:
- 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, self.DepexParser, ReportType)
-
- if not self._IsModuleBuild:
- 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", "HASH", "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
- # @param MaList The list of modules in the platform build
- #
- def AddPlatformReport(self, Wa, MaList=None):
- if self.ReportFile:
- self.ReportList.append((Wa, MaList))
-
- ##
- # 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
- # @param AutoGenTime The total time of AutoGen phase
- # @param MakeTime The total time of Make phase
- # @param GenFdsTime The total time of GenFds phase
- #
- def GenerateReport(self, BuildDuration, AutoGenTime, MakeTime, GenFdsTime):
- if self.ReportFile:
- try:
-
- if "COMPILE_INFO" in self.ReportType:
- self.GenerateCompileInfo()
-
- File = []
- for (Wa, MaList) in self.ReportList:
- PlatformReport(Wa, MaList, self.ReportType).GenerateReport(File, BuildDuration, AutoGenTime, MakeTime, GenFdsTime, self.ReportType)
- Content = FileLinesSplit(''.join(File), gLineMaxLength)
- SaveFileOnChange(self.ReportFile, Content, False)
- EdkLogger.quiet("Build report can be found at %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()))
-
-
- ##
- # Generates compile data files to be used by external tools.
- # Compile information will be generated in <Build>/<BuildTarget>/<ToolChain>/CompileInfo
- # Files generated: compile_commands.json, cscope.files, modules_report.json
- #
- # @param self The object pointer
- #
- def GenerateCompileInfo(self):
- try:
- # Lists for the output elements
- compile_commands = []
- used_files = set()
- module_report = []
-
- for (Wa, MaList) in self.ReportList:
- # Obtain list of all processed Workspace files
- for file_path in Wa._GetMetaFiles(Wa.BuildTarget, Wa.ToolChain):
- used_files.add(file_path)
-
- for autoGen in Wa.AutoGenObjectList:
-
- # Loop through all modules
- for module in (autoGen.LibraryAutoGenList + autoGen.ModuleAutoGenList):
-
- used_files.add(module.MetaFile.Path)
-
- # Main elements of module report
- module_report_data = {}
- module_report_data["Name"] = module.Name
- module_report_data["Arch"] = module.Arch
- module_report_data["Path"] = module.MetaFile.Path
- module_report_data["Guid"] = module.Guid
- module_report_data["BuildType"] = module.BuildType
- module_report_data["IsLibrary"] = module.IsLibrary
- module_report_data["SourceDir"] = module.SourceDir
- module_report_data["Files"] = []
- module_report_data["LibraryClass"] = module.Module.LibraryClass
- module_report_data["ModuleEntryPointList"] = module.Module.ModuleEntryPointList
- module_report_data["ConstructorList"] = module.Module.ConstructorList
- module_report_data["DestructorList"] = module.Module.DestructorList
-
- # Files used by module
- for data_file in module.SourceFileList:
- module_report_data["Files"].append({"Name": data_file.Name, "Path": data_file.Path})
-
- # Libraries used by module
- module_report_data["Libraries"] = []
- for data_library in module.LibraryAutoGenList:
- module_report_data["Libraries"].append({"Path": data_library.MetaFile.Path})
-
- # Packages used by module
- module_report_data["Packages"] = []
- for data_package in module.PackageList:
- module_report_data["Packages"].append({"Path": data_package.MetaFile.Path, "Includes": []})
- # Includes path used in package
- for data_package_include in data_package.Includes:
- module_report_data["Packages"][-1]["Includes"].append(data_package_include.Path)
-
- # PPI's in module
- module_report_data["PPI"] = []
- for data_ppi in module.PpiList.keys():
- module_report_data["PPI"].append({"Name": data_ppi, "Guid": module.PpiList[data_ppi]})
-
- # Protocol's in module
- module_report_data["Protocol"] = []
- for data_protocol in module.ProtocolList.keys():
- module_report_data["Protocol"].append({"Name": data_protocol, "Guid": module.ProtocolList[data_protocol]})
-
- # PCD's in module
- module_report_data["Pcd"] = []
- for data_pcd in module.LibraryPcdList:
- module_report_data["Pcd"].append({"Space": data_pcd.TokenSpaceGuidCName,
- "Name": data_pcd.TokenCName,
- "Value": data_pcd.TokenValue,
- "Guid": data_pcd.TokenSpaceGuidValue,
- "DatumType": data_pcd.DatumType,
- "Type": data_pcd.Type,
- "DefaultValue": data_pcd.DefaultValue})
- # Add module to report
- module_report.append(module_report_data)
-
- # Include file dependencies to used files
- includes_autogen = IncludesAutoGen(module.MakeFileDir, module)
- for dep in includes_autogen.DepsCollection:
- used_files.add(dep)
-
- inc_flag = "-I" # Default include flag
- if module.BuildRuleFamily == TAB_COMPILER_MSFT:
- inc_flag = "/I"
-
- for source in module.SourceFileList:
- used_files.add(source.Path)
- compile_command = {}
- if source.Ext in [".c", ".cc", ".cpp"]:
- #
- # Generate compile command for each c file
- #
- compile_command["file"] = source.Path
- compile_command["directory"] = source.Dir
- build_command = module.BuildRules[source.Ext].CommandList[0]
- build_command_variables = re.findall(r"\$\((.*?)\)", build_command)
- for var in build_command_variables:
- var_tokens = var.split("_")
- var_main = var_tokens[0]
- if len(var_tokens) == 1:
- var_value = module.BuildOption[var_main]["PATH"]
- else:
- var_value = module.BuildOption[var_main][var_tokens[1]]
- build_command = build_command.replace(f"$({var})", var_value)
- include_files = f" {inc_flag}".join(module.IncludePathList)
- build_command = build_command.replace("${src}", include_files)
- build_command = build_command.replace("${dst}", module.OutputDir)
-
- # Remove un defined macros
- compile_command["command"] = re.sub(r"\$\(.*?\)", "", build_command)
- compile_commands.append(compile_command)
-
- # Create output folder if doesn't exist
- compile_info_folder = Path(Wa.BuildDir).joinpath("CompileInfo")
- compile_info_folder.mkdir(exist_ok=True)
-
- # Sort and save files
- compile_commands.sort(key=lambda x: x["file"])
- SaveFileOnChange(compile_info_folder.joinpath(f"compile_commands.json"),json.dumps(compile_commands, indent=2), False)
-
- SaveFileOnChange(compile_info_folder.joinpath(f"cscope.files"), "\n".join(sorted(used_files)), False)
-
- module_report.sort(key=lambda x: x["Path"])
- SaveFileOnChange(compile_info_folder.joinpath(f"module_report.json"), json.dumps(module_report, indent=2), False)
-
- except:
- EdkLogger.error("BuildReport", CODE_ERROR, "Unknown fatal error when generating build report compile information", ExtraData=self.ReportFile, RaiseError=False)
- EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
-
-# This acts like the main() function for the script, unless it is 'import'ed into another script.
-if __name__ == '__main__':
- pass
+## @file +# Routines for generating build report. +# +# This module contains the functionality to generate build report after +# build all target completes successfully. +# +# Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +## Import Modules +# +import json +from pathlib import Path +import Common.LongFilePathOs as os +import re +import platform +import textwrap +import traceback +import sys +import time +import struct +import hashlib +import subprocess +import threading +from datetime import datetime +from io import BytesIO +from Common import EdkLogger +from Common.Misc import SaveFileOnChange +from Common.Misc import GuidStructureByteArrayToGuidString +from Common.Misc import GuidStructureStringToGuidString +from Common.BuildToolError import FILE_WRITE_FAILURE +from Common.BuildToolError import CODE_ERROR +from Common.BuildToolError import COMMAND_FAILURE +from Common.BuildToolError import FORMAT_INVALID +from Common.LongFilePathSupport import OpenLongFilePath as open +from Common.MultipleWorkspace import MultipleWorkspace as mws +import Common.GlobalData as GlobalData +from AutoGen.ModuleAutoGen import ModuleAutoGen +from Common.Misc import PathClass +from Common.StringUtils import NormPath +from Common.DataType import * +import collections +from Common.Expression import * +from GenFds.AprioriSection import DXE_APRIORI_GUID, PEI_APRIORI_GUID +from AutoGen.IncludesAutoGen import IncludesAutoGen + +## 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*Type=\w+\)\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 MaxLength of line in report +gLineMaxLength = 120 + +## Tags for end of line in report +gEndOfLine = "\r\n" + +## Tags for section start, end and separator +gSectionStart = ">" + "=" * (gLineMaxLength - 2) + "<" +gSectionEnd = "<" + "=" * (gLineMaxLength - 2) + ">" + "\n" +gSectionSep = "=" * gLineMaxLength + +## Tags for subsection start, end and separator +gSubSectionStart = ">" + "-" * (gLineMaxLength - 2) + "<" +gSubSectionEnd = "<" + "-" * (gLineMaxLength - 2) + ">" +gSubSectionSep = "-" * gLineMaxLength + + +## The look up table to map PCD type to pair of report display type and DEC type +gPcdTypeMap = { + TAB_PCDS_FIXED_AT_BUILD : ('FIXED', TAB_PCDS_FIXED_AT_BUILD), + TAB_PCDS_PATCHABLE_IN_MODULE: ('PATCH', TAB_PCDS_PATCHABLE_IN_MODULE), + TAB_PCDS_FEATURE_FLAG : ('FLAG', TAB_PCDS_FEATURE_FLAG), + TAB_PCDS_DYNAMIC : ('DYN', TAB_PCDS_DYNAMIC), + TAB_PCDS_DYNAMIC_HII : ('DYNHII', TAB_PCDS_DYNAMIC), + TAB_PCDS_DYNAMIC_VPD : ('DYNVPD', TAB_PCDS_DYNAMIC), + TAB_PCDS_DYNAMIC_EX : ('DEX', TAB_PCDS_DYNAMIC_EX), + TAB_PCDS_DYNAMIC_EX_HII : ('DEXHII', TAB_PCDS_DYNAMIC_EX), + TAB_PCDS_DYNAMIC_EX_VPD : ('DEXVPD', TAB_PCDS_DYNAMIC_EX), + } + +## The look up table to map module type to driver type +gDriverTypeMap = { + SUP_MODULE_SEC : '0x3 (SECURITY_CORE)', + SUP_MODULE_PEI_CORE : '0x4 (PEI_CORE)', + SUP_MODULE_PEIM : '0x6 (PEIM)', + SUP_MODULE_DXE_CORE : '0x5 (DXE_CORE)', + SUP_MODULE_DXE_DRIVER : '0x7 (DRIVER)', + SUP_MODULE_DXE_SAL_DRIVER : '0x7 (DRIVER)', + SUP_MODULE_DXE_SMM_DRIVER : '0x7 (DRIVER)', + SUP_MODULE_DXE_RUNTIME_DRIVER: '0x7 (DRIVER)', + SUP_MODULE_UEFI_DRIVER : '0x7 (DRIVER)', + SUP_MODULE_UEFI_APPLICATION : '0x9 (APPLICATION)', + SUP_MODULE_SMM_CORE : '0xD (SMM_CORE)', + 'SMM_DRIVER' : '0xA (SMM)', # Extension of module type to support PI 1.1 SMM drivers + SUP_MODULE_MM_STANDALONE : '0xE (MM_STANDALONE)', + SUP_MODULE_MM_CORE_STANDALONE : '0xF (MM_CORE_STANDALONE)' + } + +## The look up table of the supported opcode in the dependency expression binaries +gOpCodeList = ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR"] + +## Save VPD Pcd +VPDPcdList = [] + +## +# 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.append(String + gEndOfLine) + +def ByteArrayForamt(Value): + IsByteArray = False + SplitNum = 16 + ArrayList = [] + if Value.startswith('{') and Value.endswith('}') and not Value.startswith("{CODE("): + Value = Value[1:-1] + ValueList = Value.split(',') + if len(ValueList) >= SplitNum: + IsByteArray = True + if IsByteArray: + if ValueList: + Len = len(ValueList)/SplitNum + for i, element in enumerate(ValueList): + ValueList[i] = '0x%02X' % int(element.strip(), 16) + if Len: + Id = 0 + while (Id <= Len): + End = min(SplitNum*(Id+1), len(ValueList)) + Str = ','.join(ValueList[SplitNum*Id : End]) + if End == len(ValueList): + Str += '}' + ArrayList.append(Str) + break + else: + Str += ',' + ArrayList.append(Str) + Id += 1 + else: + ArrayList = [Value + '}'] + return IsByteArray, ArrayList + +## +# 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 TAB_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 + +## Split each lines in file +# +# This method is used to split the lines in file to make the length of each line +# less than MaxLength. +# +# @param Content The content of file +# @param MaxLength The Max Length of the line +# +def FileLinesSplit(Content=None, MaxLength=None): + ContentList = Content.split(TAB_LINE_BREAK) + NewContent = '' + NewContentList = [] + for Line in ContentList: + while len(Line.rstrip()) > MaxLength: + LineSpaceIndex = Line.rfind(TAB_SPACE_SPLIT, 0, MaxLength) + LineSlashIndex = Line.rfind(TAB_SLASH, 0, MaxLength) + LineBackSlashIndex = Line.rfind(TAB_BACK_SLASH, 0, MaxLength) + if max(LineSpaceIndex, LineSlashIndex, LineBackSlashIndex) > 0: + LineBreakIndex = max(LineSpaceIndex, LineSlashIndex, LineBackSlashIndex) + else: + LineBreakIndex = MaxLength + NewContentList.append(Line[:LineBreakIndex]) + Line = Line[LineBreakIndex:] + if Line: + NewContentList.append(Line) + for NewLine in NewContentList: + NewContent += NewLine + TAB_LINE_BREAK + + NewContent = NewContent.replace(gEndOfLine, TAB_LINE_BREAK).replace('\r\r\n', gEndOfLine) + return NewContent + + + +## +# Parse binary dependency expression section +# +# This utility class parses the dependency expression section and translate the readable +# GUID name and value. +# +class DepexParser(object): + ## + # Constructor function for class DepexParser + # + # This constructor function collect GUID values so that the readable + # GUID name can be translated. + # + # @param self The object pointer + # @param Wa Workspace context information + # + def __init__(self, Wa): + self._GuidDb = {} + for Pa in Wa.AutoGenObjectList: + for Package in Pa.PackageList: + for Protocol in Package.Protocols: + GuidValue = GuidStructureStringToGuidString(Package.Protocols[Protocol]) + self._GuidDb[GuidValue.upper()] = Protocol + for Ppi in Package.Ppis: + GuidValue = GuidStructureStringToGuidString(Package.Ppis[Ppi]) + self._GuidDb[GuidValue.upper()] = Ppi + for Guid in Package.Guids: + GuidValue = GuidStructureStringToGuidString(Package.Guids[Guid]) + self._GuidDb[GuidValue.upper()] = Guid + for Ma in Pa.ModuleAutoGenList: + for Pcd in Ma.FixedVoidTypePcds: + PcdValue = Ma.FixedVoidTypePcds[Pcd] + if len(PcdValue.split(',')) == 16: + GuidValue = GuidStructureByteArrayToGuidString(PcdValue) + self._GuidDb[GuidValue.upper()] = Pcd + ## + # Parse the binary dependency expression files. + # + # This function parses the binary dependency expression file and translate it + # to the instruction list. + # + # @param self The object pointer + # @param DepexFileName The file name of binary dependency expression file. + # + def ParseDepexFile(self, DepexFileName): + DepexFile = open(DepexFileName, "rb") + DepexStatement = [] + OpCode = DepexFile.read(1) + while OpCode: + Statement = gOpCodeList[struct.unpack("B", OpCode)[0]] + if Statement in ["BEFORE", "AFTER", "PUSH"]: + GuidValue = "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X" % \ + struct.unpack(PACK_PATTERN_GUID, DepexFile.read(16)) + GuidString = self._GuidDb.get(GuidValue, GuidValue) + Statement = "%s %s" % (Statement, GuidString) + DepexStatement.append(Statement) + OpCode = DepexFile.read(1) + + return DepexStatement + +## +# 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 = [] + + 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] + for LibAutoGen in M.LibraryAutoGenList: + if LibInfPath == LibAutoGen.MetaFile.Path: + LibTime = LibAutoGen.BuildTime + break + self.LibraryList.append((LibInfPath, LibClassList, LibConstructorList, LibDesstructorList, LibDepexList, LibTime)) + + ## + # 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): + if len(self.LibraryList) > 0: + FileWrite(File, gSubSectionStart) + FileWrite(File, TAB_BRG_LIBRARY) + FileWrite(File, gSubSectionSep) + for LibraryItem in self.LibraryList: + LibInfPath = LibraryItem[0] + FileWrite(File, LibInfPath) + + LibClass = LibraryItem[1] + EdkIILibInfo = "" + LibConstructor = " ".join(LibraryItem[2]) + if LibConstructor: + EdkIILibInfo += " C = " + LibConstructor + LibDestructor = " ".join(LibraryItem[3]) + if LibDestructor: + EdkIILibInfo += " D = " + LibDestructor + LibDepex = " ".join(LibraryItem[4]) + if LibDepex: + EdkIILibInfo += " Depex = " + LibDepex + if LibraryItem[5]: + EdkIILibInfo += " Time = " + LibraryItem[5] + 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 = "" + self._DepexFileName = os.path.join(M.BuildDir, "OUTPUT", M.Module.BaseName + ".depex") + ModuleType = M.ModuleType + if not ModuleType: + ModuleType = COMPONENT_TO_MODULE_MAP_DICT.get(M.ComponentType, "") + + if ModuleType in [SUP_MODULE_SEC, SUP_MODULE_PEI_CORE, SUP_MODULE_DXE_CORE, SUP_MODULE_SMM_CORE, SUP_MODULE_MM_CORE_STANDALONE, SUP_MODULE_UEFI_APPLICATION]: + 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.DepexExpressionDict.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 + # @param GlobalDepexParser The platform global Dependency expression parser object + # + def GenerateReport(self, File, GlobalDepexParser): + if not self.Depex: + return + FileWrite(File, gSubSectionStart) + if os.path.isfile(self._DepexFileName): + try: + DepexStatements = GlobalDepexParser.ParseDepexFile(self._DepexFileName) + FileWrite(File, "Final Dependency Expression (DEPEX) Instructions") + for DepexStatement in DepexStatements: + FileWrite(File, " %s" % DepexStatement) + FileWrite(File, gSubSectionSep) + except: + EdkLogger.warn(None, "Dependency expression file is corrupted", self._DepexFileName) + + FileWrite(File, "Dependency Expression (DEPEX) from %s" % self.Source) + + if self.Source == "INF": + FileWrite(File, 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, 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.ModuleArch = M.Arch + self.FileGuid = M.Guid + self.Size = 0 + self.BuildTimeStamp = None + self.Hash = 0 + self.DriverType = "" + if not M.IsLibrary: + ModuleType = M.ModuleType + if not ModuleType: + ModuleType = COMPONENT_TO_MODULE_MAP_DICT.get(M.ComponentType, "") + # + # If a module complies to PI 1.1, promote Module type to "SMM_DRIVER" + # + if ModuleType == SUP_MODULE_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, "0x2 (FREE_FORM)") + 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.BuildTime = M.BuildTime + + 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 report object + # @param GlobalPredictionReport The platform global Prediction report object + # @param GlobalDepexParser The platform global Dependency expression parser object + # @param ReportType The kind of report items in the final report file + # + def GenerateReport(self, File, GlobalPcdReport, GlobalPredictionReport, GlobalDepexParser, ReportType): + FileWrite(File, gSectionStart) + + FwReportFileName = os.path.join(self._BuildDir, "OUTPUT", 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.utcfromtimestamp(int(Match.group(1))) + except IOError: + EdkLogger.warn(None, "Fail to read report file", FwReportFileName) + + if "HASH" in ReportType: + OutputDir = os.path.join(self._BuildDir, "OUTPUT") + DefaultEFIfile = os.path.join(OutputDir, self.ModuleName + ".efi") + if os.path.isfile(DefaultEFIfile): + Tempfile = os.path.join(OutputDir, self.ModuleName + "_hash.tmp") + # rebase the efi image since its base address may not zero + cmd = ["GenFw", "--rebase", str(0), "-o", Tempfile, DefaultEFIfile] + try: + PopenObject = subprocess.Popen(' '.join(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + except Exception as X: + EdkLogger.error("GenFw", COMMAND_FAILURE, ExtraData="%s: %s" % (str(X), cmd[0])) + EndOfProcedure = threading.Event() + EndOfProcedure.clear() + if PopenObject.stderr: + StdErrThread = threading.Thread(target=ReadMessage, args=(PopenObject.stderr, EdkLogger.quiet, EndOfProcedure)) + StdErrThread.setName("STDERR-Redirector") + StdErrThread.setDaemon(False) + StdErrThread.start() + # waiting for program exit + PopenObject.wait() + if PopenObject.stderr: + StdErrThread.join() + if PopenObject.returncode != 0: + EdkLogger.error("GenFw", COMMAND_FAILURE, "Failed to generate firmware hash image for %s" % (DefaultEFIfile)) + if os.path.isfile(Tempfile): + self.Hash = hashlib.sha1() + buf = open(Tempfile, 'rb').read() + if self.Hash.update(buf): + self.Hash = self.Hash.update(buf) + self.Hash = self.Hash.hexdigest() + os.remove(Tempfile) + + FileWrite(File, "Module Summary") + FileWrite(File, "Module Name: %s" % self.ModuleName) + FileWrite(File, "Module Arch: %s" % self.ModuleArch) + 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.Hash: + FileWrite(File, "SHA1 HASH: %s *%s" % (self.Hash, self.ModuleName + ".efi")) + if self.BuildTimeStamp: + FileWrite(File, "Build Time Stamp: %s" % self.BuildTimeStamp) + if self.BuildTime: + FileWrite(File, "Module Build Time: %s" % self.BuildTime) + 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,self.FileGuid) + + if "LIBRARY" in ReportType: + self.LibraryReport.GenerateReport(File) + + if "DEPEX" in ReportType: + self.DepexReport.GenerateReport(File, GlobalDepexParser) + + 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) + +def ReadMessage(From, To, ExitFlag): + while True: + # read one line a time + Line = From.readline() + # empty string means "end" + if Line is not None and Line != b"": + To(Line.rstrip().decode(encoding='utf-8', errors='ignore')) + else: + break + if ExitFlag.isSet(): + break + +## +# 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.UnusedPcds = {} + self.ConditionalPcds = {} + self.MaxLen = 0 + self.Arch = None + if Wa.FdfProfile: + self.FdfPcdSet = Wa.FdfProfile.PcdDict + else: + self.FdfPcdSet = {} + + self.DefaultStoreSingle = True + self.SkuSingle = True + if GlobalData.gDefaultStores and len(GlobalData.gDefaultStores) > 1: + self.DefaultStoreSingle = False + if GlobalData.gSkuids and len(GlobalData.gSkuids) > 1: + self.SkuSingle = False + + self.ModulePcdOverride = {} + for Pa in Wa.AutoGenObjectList: + self.Arch = Pa.Arch + # + # 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) + # + # Collect the PCD defined in DSC/FDF file, but not used in module + # + UnusedPcdFullList = [] + StructPcdDict = GlobalData.gStructurePcd.get(self.Arch, collections.OrderedDict()) + for Name, Guid in StructPcdDict: + if (Name, Guid) not in Pa.Platform.Pcds: + Pcd = StructPcdDict[(Name, Guid)] + PcdList = self.AllPcds.setdefault(Guid, {}).setdefault(Pcd.Type, []) + if Pcd not in PcdList and Pcd not in UnusedPcdFullList: + UnusedPcdFullList.append(Pcd) + for item in Pa.Platform.Pcds: + Pcd = Pa.Platform.Pcds[item] + if not Pcd.Type: + # check the Pcd in FDF file, whether it is used in module first + for T in PCD_TYPE_LIST: + PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(T, []) + if Pcd in PcdList: + Pcd.Type = T + break + if not Pcd.Type: + PcdTypeFlag = False + for package in Pa.PackageList: + for T in PCD_TYPE_LIST: + if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, T) in package.Pcds: + Pcd.Type = T + PcdTypeFlag = True + if not Pcd.DatumType: + Pcd.DatumType = package.Pcds[(Pcd.TokenCName, Pcd.TokenSpaceGuidCName, T)].DatumType + break + if PcdTypeFlag: + break + if not Pcd.DatumType: + PcdType = Pcd.Type + # Try to remove Hii and Vpd suffix + if PcdType.startswith(TAB_PCDS_DYNAMIC_EX): + PcdType = TAB_PCDS_DYNAMIC_EX + elif PcdType.startswith(TAB_PCDS_DYNAMIC): + PcdType = TAB_PCDS_DYNAMIC + for package in Pa.PackageList: + if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, PcdType) in package.Pcds: + Pcd.DatumType = package.Pcds[(Pcd.TokenCName, Pcd.TokenSpaceGuidCName, PcdType)].DatumType + break + + PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, []) + UnusedPcdList = self.UnusedPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, []) + if Pcd in UnusedPcdList: + UnusedPcdList.remove(Pcd) + if Pcd not in PcdList and Pcd not in UnusedPcdFullList: + UnusedPcdFullList.append(Pcd) + if len(Pcd.TokenCName) > self.MaxLen: + self.MaxLen = len(Pcd.TokenCName) + + if GlobalData.gConditionalPcds: + for PcdItem in GlobalData.gConditionalPcds: + if '.' in PcdItem: + (TokenSpaceGuidCName, TokenCName) = PcdItem.split('.') + if (TokenCName, TokenSpaceGuidCName) in Pa.Platform.Pcds: + Pcd = Pa.Platform.Pcds[(TokenCName, TokenSpaceGuidCName)] + PcdList = self.ConditionalPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, []) + if Pcd not in PcdList: + PcdList.append(Pcd) + + UnusedPcdList = [] + if UnusedPcdFullList: + for Pcd in UnusedPcdFullList: + if Pcd.TokenSpaceGuidCName + '.' + Pcd.TokenCName in GlobalData.gConditionalPcds: + continue + UnusedPcdList.append(Pcd) + + for Pcd in UnusedPcdList: + PcdList = self.UnusedPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, []) + if Pcd not in PcdList: + PcdList.append(Pcd) + + 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 = {} + self._GuidDict = {} + for Pa in Wa.AutoGenObjectList: + for Package in Pa.PackageList: + Guids = Package.Guids + self._GuidDict.update(Guids) + 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 Pa in Wa.AutoGenObjectList: + for (TokenCName, TokenSpaceGuidCName) in Pa.Platform.Pcds: + DscDefaultValue = Pa.Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DscDefaultValue + if DscDefaultValue: + self.DscPcdDefault[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue + + def GenerateReport(self, File, ModulePcdSet,ModuleGuid=None): + if not ModulePcdSet: + if self.ConditionalPcds: + self.GenerateReportDetail(File, ModulePcdSet, 1) + if self.UnusedPcds: + IsEmpty = True + for Token in self.UnusedPcds: + TokenDict = self.UnusedPcds[Token] + for Type in TokenDict: + if TokenDict[Type]: + IsEmpty = False + break + if not IsEmpty: + break + if not IsEmpty: + self.GenerateReportDetail(File, ModulePcdSet, 2) + self.GenerateReportDetail(File, ModulePcdSet,ModuleGuid = ModuleGuid) + + ## + # 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 ReportySubType 0 means platform/module PCD report, 1 means Conditional + # directives section report, 2 means Unused Pcds section report + # @param DscOverridePcds Module DSC override PCDs set + # + def GenerateReportDetail(self, File, ModulePcdSet, ReportSubType = 0,ModuleGuid=None): + PcdDict = self.AllPcds + if ReportSubType == 1: + PcdDict = self.ConditionalPcds + elif ReportSubType == 2: + PcdDict = self.UnusedPcds + + if not ModulePcdSet: + FileWrite(File, gSectionStart) + if ReportSubType == 1: + FileWrite(File, "Conditional Directives used by the build system") + elif ReportSubType == 2: + FileWrite(File, "PCDs not used by modules or in conditional directives") + else: + FileWrite(File, "Platform Configuration Database Report") + + FileWrite(File, " *B - PCD override in the build option") + FileWrite(File, " *P - Platform scoped PCD override in DSC file") + FileWrite(File, " *F - Platform scoped PCD override in FDF file") + if not ReportSubType: + FileWrite(File, " *M - Module scoped PCD override") + FileWrite(File, gSectionSep) + else: + if not ReportSubType and ModulePcdSet: + # + # For module PCD sub-section + # + FileWrite(File, gSubSectionStart) + FileWrite(File, TAB_BRG_PCD) + FileWrite(File, gSubSectionSep) + AllPcdDict = {} + for Key in PcdDict: + AllPcdDict[Key] = {} + for Type in PcdDict[Key]: + for Pcd in PcdDict[Key][Type]: + AllPcdDict[Key][(Pcd.TokenCName, Type)] = Pcd + for Key in sorted(AllPcdDict): + # + # Group PCD by their token space GUID C Name + # + First = True + for PcdTokenCName, Type in sorted(AllPcdDict[Key]): + # + # Group PCD by their usage type + # + Pcd = AllPcdDict[Key][(PcdTokenCName, Type)] + TypeName, DecType = gPcdTypeMap.get(Type, ("", Type)) + MixedPcdFlag = False + if GlobalData.MixedPcd: + for PcdKey in GlobalData.MixedPcd: + if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName) in GlobalData.MixedPcd[PcdKey]: + PcdTokenCName = PcdKey[0] + MixedPcdFlag = True + if MixedPcdFlag and not ModulePcdSet: + continue + # + # 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)) + DscDefaultValBak = DscDefaultValue + Field = '' + for (CName, Guid, Field) in self.FdfPcdSet: + if CName == PcdTokenCName and Guid == Key: + DscDefaultValue = self.FdfPcdSet[(CName, Guid, Field)] + break + if DscDefaultValue != DscDefaultValBak: + try: + DscDefaultValue = ValueExpressionEx(DscDefaultValue, Pcd.DatumType, self._GuidDict)(True) + except BadExpression as DscDefaultValue: + EdkLogger.error('BuildReport', FORMAT_INVALID, "PCD Value: %s, Type: %s" %(DscDefaultValue, Pcd.DatumType)) + + InfDefaultValue = None + + PcdValue = DecDefaultValue + if DscDefaultValue: + PcdValue = DscDefaultValue + #The DefaultValue of StructurePcd already be the latest, no need to update. + if not self.IsStructurePcd(Pcd.TokenCName, Pcd.TokenSpaceGuidCName): + Pcd.DefaultValue = PcdValue + PcdComponentValue = None + if ModulePcdSet is not None: + if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type) not in ModulePcdSet: + continue + InfDefaultValue, PcdComponentValue = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type] + PcdValue = PcdComponentValue + #The DefaultValue of StructurePcd already be the latest, no need to update. + if not self.IsStructurePcd(Pcd.TokenCName, Pcd.TokenSpaceGuidCName): + Pcd.DefaultValue = PcdValue + if InfDefaultValue: + try: + InfDefaultValue = ValueExpressionEx(InfDefaultValue, Pcd.DatumType, self._GuidDict)(True) + except BadExpression as InfDefaultValue: + EdkLogger.error('BuildReport', FORMAT_INVALID, "PCD Value: %s, Type: %s" % (InfDefaultValue, Pcd.DatumType)) + if InfDefaultValue == "": + InfDefaultValue = None + + BuildOptionMatch = False + if GlobalData.BuildOptionPcd: + for pcd in GlobalData.BuildOptionPcd: + if (Pcd.TokenSpaceGuidCName, Pcd.TokenCName) == (pcd[0], pcd[1]): + if pcd[2]: + continue + PcdValue = pcd[3] + #The DefaultValue of StructurePcd already be the latest, no need to update. + if not self.IsStructurePcd(Pcd.TokenCName, Pcd.TokenSpaceGuidCName): + Pcd.DefaultValue = PcdValue + BuildOptionMatch = True + break + + if First: + if ModulePcdSet is None: + FileWrite(File, "") + FileWrite(File, Key) + First = False + + + if Pcd.DatumType in TAB_PCD_NUMERIC_TYPES: + if PcdValue.startswith('0') and not PcdValue.lower().startswith('0x') and \ + len(PcdValue) > 1 and PcdValue.lstrip('0'): + PcdValue = PcdValue.lstrip('0') + PcdValueNumber = int(PcdValue.strip(), 0) + if DecDefaultValue is None: + DecMatch = True + else: + if DecDefaultValue.startswith('0') and not DecDefaultValue.lower().startswith('0x') and \ + len(DecDefaultValue) > 1 and DecDefaultValue.lstrip('0'): + DecDefaultValue = DecDefaultValue.lstrip('0') + DecDefaultValueNumber = int(DecDefaultValue.strip(), 0) + DecMatch = (DecDefaultValueNumber == PcdValueNumber) + + if InfDefaultValue is None: + InfMatch = True + else: + if InfDefaultValue.startswith('0') and not InfDefaultValue.lower().startswith('0x') and \ + len(InfDefaultValue) > 1 and InfDefaultValue.lstrip('0'): + InfDefaultValue = InfDefaultValue.lstrip('0') + InfDefaultValueNumber = int(InfDefaultValue.strip(), 0) + InfMatch = (InfDefaultValueNumber == PcdValueNumber) + + if DscDefaultValue is None: + DscMatch = True + else: + if DscDefaultValue.startswith('0') and not DscDefaultValue.lower().startswith('0x') and \ + len(DscDefaultValue) > 1 and DscDefaultValue.lstrip('0'): + DscDefaultValue = DscDefaultValue.lstrip('0') + DscDefaultValueNumber = int(DscDefaultValue.strip(), 0) + DscMatch = (DscDefaultValueNumber == PcdValueNumber) + else: + if DecDefaultValue is None: + DecMatch = True + else: + DecMatch = (DecDefaultValue.strip() == PcdValue.strip()) + + if InfDefaultValue is None: + InfMatch = True + else: + InfMatch = (InfDefaultValue.strip() == PcdValue.strip()) + + if DscDefaultValue is None: + DscMatch = True + else: + DscMatch = (DscDefaultValue.strip() == PcdValue.strip()) + + IsStructure = False + if self.IsStructurePcd(Pcd.TokenCName, Pcd.TokenSpaceGuidCName): + IsStructure = True + if TypeName in ('DYNVPD', 'DEXVPD'): + SkuInfoList = Pcd.SkuInfoList + Pcd = GlobalData.gStructurePcd[self.Arch][(Pcd.TokenCName, Pcd.TokenSpaceGuidCName)] + if ModulePcdSet and ModulePcdSet.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type)): + InfDefaultValue, PcdComponentValue = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type] + DscDefaultValBak = Pcd.DefaultValue + Pcd.DefaultValue = PcdComponentValue + + Pcd.DatumType = Pcd.StructName + if TypeName in ('DYNVPD', 'DEXVPD'): + Pcd.SkuInfoList = SkuInfoList + if Pcd.PcdValueFromComm or Pcd.PcdFieldValueFromComm: + BuildOptionMatch = True + DecMatch = False + elif Pcd.PcdValueFromFdf or Pcd.PcdFieldValueFromFdf: + DscDefaultValue = True + DscMatch = True + DecMatch = False + else: + if Pcd.Type in PCD_DYNAMIC_TYPE_SET | PCD_DYNAMIC_EX_TYPE_SET: + DscOverride = False + if Pcd.DefaultFromDSC: + DscOverride = True + else: + DictLen = 0 + for item in Pcd.SkuOverrideValues: + DictLen += len(Pcd.SkuOverrideValues[item]) + if not DictLen: + DscOverride = False + else: + if not Pcd.SkuInfoList: + OverrideValues = Pcd.SkuOverrideValues + if OverrideValues: + for Data in OverrideValues.values(): + Struct = list(Data.values()) + if Struct: + DscOverride = self.ParseStruct(Struct[0]) + break + else: + SkuList = sorted(Pcd.SkuInfoList.keys()) + for Sku in SkuList: + SkuInfo = Pcd.SkuInfoList[Sku] + if SkuInfo.DefaultStoreDict: + DefaultStoreList = sorted(SkuInfo.DefaultStoreDict.keys()) + for DefaultStore in DefaultStoreList: + OverrideValues = Pcd.SkuOverrideValues.get(Sku) + if OverrideValues: + DscOverride = self.ParseStruct(OverrideValues[DefaultStore]) + if DscOverride: + break + if DscOverride: + break + if DscOverride: + DscDefaultValue = True + DscMatch = True + DecMatch = False + else: + DecMatch = True + else: + if Pcd.DscRawValue or (ModuleGuid and ModuleGuid.replace("-","S") in Pcd.PcdValueFromComponents): + DscDefaultValue = True + DscMatch = True + DecMatch = False + else: + DscDefaultValue = False + DecMatch = True + + # + # Report PCD item according to their override relationship + # + if Pcd.DatumType == 'BOOLEAN': + if DscDefaultValue: + DscDefaultValue = str(int(DscDefaultValue, 0)) + if DecDefaultValue: + DecDefaultValue = str(int(DecDefaultValue, 0)) + if InfDefaultValue: + InfDefaultValue = str(int(InfDefaultValue, 0)) + if Pcd.DefaultValue: + Pcd.DefaultValue = str(int(Pcd.DefaultValue, 0)) + if DecMatch: + self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, ' ') + elif InfDefaultValue and InfMatch: + self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '*M') + elif BuildOptionMatch: + self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '*B') + else: + if PcdComponentValue: + self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, PcdComponentValue, DecMatch, DecDefaultValue, '*M', ModuleGuid) + elif DscDefaultValue and DscMatch: + if (Pcd.TokenCName, Key, Field) in self.FdfPcdSet: + self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '*F') + else: + self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '*P') + + + if ModulePcdSet is None: + if IsStructure: + continue + if not TypeName in ('PATCH', 'FLAG', 'FIXED'): + continue + if not BuildOptionMatch: + ModuleOverride = self.ModulePcdOverride.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName), {}) + for ModulePath in ModuleOverride: + ModuleDefault = ModuleOverride[ModulePath] + if Pcd.DatumType in TAB_PCD_NUMERIC_TYPES: + if ModuleDefault.startswith('0') and not ModuleDefault.lower().startswith('0x') and \ + len(ModuleDefault) > 1 and ModuleDefault.lstrip('0'): + ModuleDefault = ModuleDefault.lstrip('0') + ModulePcdDefaultValueNumber = int(ModuleDefault.strip(), 0) + Match = (ModulePcdDefaultValueNumber == PcdValueNumber) + if Pcd.DatumType == 'BOOLEAN': + ModuleDefault = str(ModulePcdDefaultValueNumber) + else: + Match = (ModuleDefault.strip() == PcdValue.strip()) + if Match: + continue + IsByteArray, ArrayList = ByteArrayForamt(ModuleDefault.strip()) + if IsByteArray: + FileWrite(File, ' *M %-*s = %s' % (self.MaxLen + 15, ModulePath, '{')) + for Array in ArrayList: + FileWrite(File, Array) + else: + Value = ModuleDefault.strip() + if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES: + if Value.startswith(('0x', '0X')): + Value = '{} ({:d})'.format(Value, int(Value, 0)) + else: + Value = "0x{:X} ({})".format(int(Value, 0), Value) + FileWrite(File, ' *M %-*s = %s' % (self.MaxLen + 15, ModulePath, Value)) + + if ModulePcdSet is None: + FileWrite(File, gSectionEnd) + else: + if not ReportSubType and ModulePcdSet: + FileWrite(File, gSubSectionEnd) + + def ParseStruct(self, struct): + HasDscOverride = False + if struct: + for _, Values in list(struct.items()): + for Key, value in Values.items(): + if value[1] and value[1].endswith('.dsc'): + HasDscOverride = True + break + if HasDscOverride == True: + break + return HasDscOverride + + def PrintPcdDefault(self, File, Pcd, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue): + if not DscMatch and DscDefaultValue is not None: + Value = DscDefaultValue.strip() + IsByteArray, ArrayList = ByteArrayForamt(Value) + if IsByteArray: + FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', "{")) + for Array in ArrayList: + FileWrite(File, Array) + else: + if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES: + if Value.startswith(('0x', '0X')): + Value = '{} ({:d})'.format(Value, int(Value, 0)) + else: + Value = "0x{:X} ({})".format(int(Value, 0), Value) + FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', Value)) + if not InfMatch and InfDefaultValue is not None: + Value = InfDefaultValue.strip() + IsByteArray, ArrayList = ByteArrayForamt(Value) + if IsByteArray: + FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', "{")) + for Array in ArrayList: + FileWrite(File, Array) + else: + if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES: + if Value.startswith(('0x', '0X')): + Value = '{} ({:d})'.format(Value, int(Value, 0)) + else: + Value = "0x{:X} ({})".format(int(Value, 0), Value) + FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', Value)) + + if not DecMatch and DecDefaultValue is not None: + Value = DecDefaultValue.strip() + IsByteArray, ArrayList = ByteArrayForamt(Value) + if IsByteArray: + FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', "{")) + for Array in ArrayList: + FileWrite(File, Array) + else: + if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES: + if Value.startswith(('0x', '0X')): + Value = '{} ({:d})'.format(Value, int(Value, 0)) + else: + Value = "0x{:X} ({})".format(int(Value, 0), Value) + FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', Value)) + if IsStructure: + for filedvalues in Pcd.DefaultValues.values(): + self.PrintStructureInfo(File, filedvalues) + if DecMatch and IsStructure: + for filedvalues in Pcd.DefaultValues.values(): + self.PrintStructureInfo(File, filedvalues) + + def PrintPcdValue(self, File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, Flag = ' ',ModuleGuid=None): + if not Pcd.SkuInfoList: + Value = Pcd.DefaultValue + IsByteArray, ArrayList = ByteArrayForamt(Value) + if IsByteArray: + FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '{')) + for Array in ArrayList: + FileWrite(File, Array) + else: + if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES: + if Value.startswith('0') and not Value.lower().startswith('0x') and len(Value) > 1 and Value.lstrip('0'): + Value = Value.lstrip('0') + if Value.startswith(('0x', '0X')): + Value = '{} ({:d})'.format(Value, int(Value, 0)) + else: + Value = "0x{:X} ({})".format(int(Value, 0), Value) + FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', Value)) + if IsStructure: + FiledOverrideFlag = False + if (Pcd.TokenCName,Pcd.TokenSpaceGuidCName) in GlobalData.gPcdSkuOverrides: + OverrideValues = GlobalData.gPcdSkuOverrides[(Pcd.TokenCName,Pcd.TokenSpaceGuidCName)] + else: + OverrideValues = Pcd.SkuOverrideValues + FieldOverrideValues = None + if OverrideValues: + for Data in OverrideValues.values(): + Struct = list(Data.values()) + if Struct: + FieldOverrideValues = Struct[0] + FiledOverrideFlag = True + break + if Pcd.PcdFiledValueFromDscComponent and ModuleGuid and ModuleGuid.replace("-","S") in Pcd.PcdFiledValueFromDscComponent: + FieldOverrideValues = Pcd.PcdFiledValueFromDscComponent[ModuleGuid.replace("-","S")] + if FieldOverrideValues: + OverrideFieldStruct = self.OverrideFieldValue(Pcd, FieldOverrideValues) + self.PrintStructureInfo(File, OverrideFieldStruct) + + if not FiledOverrideFlag and (Pcd.PcdFieldValueFromComm or Pcd.PcdFieldValueFromFdf): + OverrideFieldStruct = self.OverrideFieldValue(Pcd, {}) + self.PrintStructureInfo(File, OverrideFieldStruct) + self.PrintPcdDefault(File, Pcd, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue) + else: + FirstPrint = True + SkuList = sorted(Pcd.SkuInfoList.keys()) + for Sku in SkuList: + SkuInfo = Pcd.SkuInfoList[Sku] + SkuIdName = SkuInfo.SkuIdName + if TypeName in ('DYNHII', 'DEXHII'): + if SkuInfo.DefaultStoreDict: + DefaultStoreList = sorted(SkuInfo.DefaultStoreDict.keys()) + for DefaultStore in DefaultStoreList: + Value = SkuInfo.DefaultStoreDict[DefaultStore] + IsByteArray, ArrayList = ByteArrayForamt(Value) + if Pcd.DatumType == 'BOOLEAN': + Value = str(int(Value, 0)) + if FirstPrint: + FirstPrint = False + if IsByteArray: + if self.DefaultStoreSingle and self.SkuSingle: + FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '{')) + elif self.DefaultStoreSingle and not self.SkuSingle: + FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '{')) + elif not self.DefaultStoreSingle and self.SkuSingle: + FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + DefaultStore + ')', '{')) + else: + FileWrite(File, ' %-*s : %6s %10s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '(' + DefaultStore + ')', '{')) + for Array in ArrayList: + FileWrite(File, Array) + else: + if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES: + if Value.startswith(('0x', '0X')): + Value = '{} ({:d})'.format(Value, int(Value, 0)) + else: + Value = "0x{:X} ({})".format(int(Value, 0), Value) + if self.DefaultStoreSingle and self.SkuSingle: + FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', Value)) + elif self.DefaultStoreSingle and not self.SkuSingle: + FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', Value)) + elif not self.DefaultStoreSingle and self.SkuSingle: + FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + DefaultStore + ')', Value)) + else: + FileWrite(File, ' %-*s : %6s %10s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '(' + DefaultStore + ')', Value)) + else: + if IsByteArray: + if self.DefaultStoreSingle and self.SkuSingle: + FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '{')) + elif self.DefaultStoreSingle and not self.SkuSingle: + FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '{')) + elif not self.DefaultStoreSingle and self.SkuSingle: + FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + DefaultStore + ')', '{')) + else: + FileWrite(File, ' %-*s : %6s %10s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '(' + DefaultStore + ')', '{')) + for Array in ArrayList: + FileWrite(File, Array) + else: + if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES: + if Value.startswith(('0x', '0X')): + Value = '{} ({:d})'.format(Value, int(Value, 0)) + else: + Value = "0x{:X} ({})".format(int(Value, 0), Value) + if self.DefaultStoreSingle and self.SkuSingle: + FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', Value)) + elif self.DefaultStoreSingle and not self.SkuSingle: + FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', Value)) + elif not self.DefaultStoreSingle and self.SkuSingle: + FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + DefaultStore + ')', Value)) + else: + FileWrite(File, ' %-*s : %6s %10s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '(' + DefaultStore + ')', Value)) + FileWrite(File, '%*s: %s: %s' % (self.MaxLen + 4, SkuInfo.VariableGuid, SkuInfo.VariableName, SkuInfo.VariableOffset)) + if IsStructure: + OverrideValues = Pcd.SkuOverrideValues.get(Sku) + if OverrideValues: + OverrideFieldStruct = self.OverrideFieldValue(Pcd, OverrideValues[DefaultStore]) + self.PrintStructureInfo(File, OverrideFieldStruct) + self.PrintPcdDefault(File, Pcd, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue) + else: + Value = SkuInfo.DefaultValue + IsByteArray, ArrayList = ByteArrayForamt(Value) + if Pcd.DatumType == 'BOOLEAN': + Value = str(int(Value, 0)) + if FirstPrint: + FirstPrint = False + if IsByteArray: + if self.SkuSingle: + FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', "{")) + else: + FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', "{")) + for Array in ArrayList: + FileWrite(File, Array) + else: + if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES: + if Value.startswith(('0x', '0X')): + Value = '{} ({:d})'.format(Value, int(Value, 0)) + else: + Value = "0x{:X} ({})".format(int(Value, 0), Value) + if self.SkuSingle: + FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', Value)) + else: + FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', Value)) + else: + if IsByteArray: + if self.SkuSingle: + FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', "{")) + else: + FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', "{")) + for Array in ArrayList: + FileWrite(File, Array) + else: + if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES: + if Value.startswith(('0x', '0X')): + Value = '{} ({:d})'.format(Value, int(Value, 0)) + else: + Value = "0x{:X} ({})".format(int(Value, 0), Value) + if self.SkuSingle: + FileWrite(File, ' %-*s : %6s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', Value)) + else: + FileWrite(File, ' %-*s : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', Value)) + if TypeName in ('DYNVPD', 'DEXVPD'): + FileWrite(File, '%*s' % (self.MaxLen + 4, SkuInfo.VpdOffset)) + VPDPcdItem = (Pcd.TokenSpaceGuidCName + '.' + PcdTokenCName, SkuIdName, SkuInfo.VpdOffset, Pcd.MaxDatumSize, SkuInfo.DefaultValue) + if VPDPcdItem not in VPDPcdList: + PcdGuidList = self.UnusedPcds.get(Pcd.TokenSpaceGuidCName) + if PcdGuidList: + PcdList = PcdGuidList.get(Pcd.Type) + if not PcdList: + VPDPcdList.append(VPDPcdItem) + for VpdPcd in PcdList: + if PcdTokenCName == VpdPcd.TokenCName: + break + else: + VPDPcdList.append(VPDPcdItem) + if IsStructure: + FiledOverrideFlag = False + OverrideValues = Pcd.SkuOverrideValues.get(Sku) + if OverrideValues: + Keys = list(OverrideValues.keys()) + OverrideFieldStruct = self.OverrideFieldValue(Pcd, OverrideValues[Keys[0]]) + self.PrintStructureInfo(File, OverrideFieldStruct) + FiledOverrideFlag = True + if not FiledOverrideFlag and (Pcd.PcdFieldValueFromComm or Pcd.PcdFieldValueFromFdf): + OverrideFieldStruct = self.OverrideFieldValue(Pcd, {}) + self.PrintStructureInfo(File, OverrideFieldStruct) + self.PrintPcdDefault(File, Pcd, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue) + + def OverrideFieldValue(self, Pcd, OverrideStruct): + OverrideFieldStruct = collections.OrderedDict() + if OverrideStruct: + for _, Values in OverrideStruct.items(): + for Key,value in Values.items(): + if value[1] and value[1].endswith('.dsc'): + OverrideFieldStruct[Key] = value + if Pcd.PcdFieldValueFromFdf: + for Key, Values in Pcd.PcdFieldValueFromFdf.items(): + if Key in OverrideFieldStruct and Values[0] == OverrideFieldStruct[Key][0]: + continue + OverrideFieldStruct[Key] = Values + if Pcd.PcdFieldValueFromComm: + for Key, Values in Pcd.PcdFieldValueFromComm.items(): + if Key in OverrideFieldStruct and Values[0] == OverrideFieldStruct[Key][0]: + continue + OverrideFieldStruct[Key] = Values + return OverrideFieldStruct + + def PrintStructureInfo(self, File, Struct): + for Key, Value in sorted(Struct.items(), key=lambda x: x[0]): + if Value[1] and 'build command options' in Value[1]: + FileWrite(File, ' *B %-*s = %s' % (self.MaxLen + 4, '.' + Key, Value[0])) + elif Value[1] and Value[1].endswith('.fdf'): + FileWrite(File, ' *F %-*s = %s' % (self.MaxLen + 4, '.' + Key, Value[0])) + else: + FileWrite(File, ' %-*s = %s' % (self.MaxLen + 4, '.' + Key, Value[0])) + + def StrtoHex(self, value): + try: + value = hex(int(value)) + return value + except: + if value.startswith("L\"") and value.endswith("\""): + valuelist = [] + for ch in value[2:-1]: + valuelist.append(hex(ord(ch))) + valuelist.append('0x00') + return valuelist + elif value.startswith("\"") and value.endswith("\""): + return hex(ord(value[1:-1])) + elif value.startswith("{") and value.endswith("}"): + valuelist = [] + if ',' not in value: + return value[1:-1] + for ch in value[1:-1].split(','): + ch = ch.strip() + if ch.startswith('0x') or ch.startswith('0X'): + valuelist.append(ch) + continue + try: + valuelist.append(hex(int(ch.strip()))) + except: + pass + return valuelist + else: + return value + + def IsStructurePcd(self, PcdToken, PcdTokenSpaceGuid): + if GlobalData.gStructurePcd and (self.Arch in GlobalData.gStructurePcd) and ((PcdToken, PcdTokenSpaceGuid) in GlobalData.gStructurePcd[self.Arch]): + return True + else: + return False + +## +# 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: + # + # BASE typed modules are EFI agnostic, so we need not scan + # their source code to find PPI/Protocol produce or consume + # information. + # + if Module.ModuleType == SUP_MODULE_BASE: + continue + # + # 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) + + RealEntryPoint = "_ModuleEntryPoint" + + 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 != BINARY_FILE_TYPE_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 = [] + for Item in self._SourceList: + FileWrite(TempFile, Item) + SaveFileOnChange(SourceList, "".join(TempFile), False) + TempFile = [] + for Key in self._GuidMap: + FileWrite(TempFile, "%s %s" % (Key, self._GuidMap[Key])) + SaveFileOnChange(GuidList, "".join(TempFile), False) + + try: + from Eot.EotMain import Eot + + # + # Invoke EOT tool and echo its runtime performance + # + EotStartTime = time.time() + Eot(CommandLineOption=False, SourceFileList=SourceList, GuidList=GuidList, + FvFileList=' '.join(FvFileList), Dispatch=DispatchList, IsInit=True) + EotEndTime = time.time() + EotDuration = time.strftime("%H:%M:%S", time.gmtime(int(round(EotEndTime - EotStartTime)))) + EdkLogger.quiet("EOT run time: %s\n" % EotDuration) + + # + # 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): + FvDictKey=FvName.upper() + if FvDictKey in Wa.FdfProfile.FvDict: + 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 + self._WorkspaceDir = Wa.WorkspaceDir + + # + # If the input FdRegion is not a firmware volume, + # we are done. + # + if self.Type != BINARY_FILE_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 Pa in Wa.AutoGenObjectList: + for Package in Pa.PackageList: + for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds: + DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue + PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue + # + # Collect PCDs defined in DSC file + # + for Pa in Wa.AutoGenObjectList: + for (TokenCName, TokenSpaceGuidCName) in Pa.Platform.Pcds: + DscDefaultValue = Pa.Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue + PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue + + # + # Add PEI and DXE a priori files GUIDs defined in PI specification. + # + self._GuidsDb[PEI_APRIORI_GUID] = "PEI Apriori" + self._GuidsDb[DXE_APRIORI_GUID] = "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 = mws.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: + FvDictKey=FvName.upper() + if FvDictKey in Wa.FdfProfile.FvDict: + 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 = mws.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 == BINARY_FILE_TYPE_FV: + FvTotalSize = 0 + FvTakenSize = 0 + FvFreeSize = 0 + if FvName.upper().endswith('.FV'): + FileExt = FvName + ".txt" + else: + FileExt = FvName + ".Fv.txt" + + if not os.path.isfile(FileExt): + FvReportFileName = mws.join(self._WorkspaceDir, FileExt) + if not os.path.isfile(FvReportFileName): + FvReportFileName = os.path.join(self._FvDir, FileExt) + 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 = sorted(OffsetInfo.keys()) + 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], TAB_FV_DIRECTORY, 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] + self.FvPath = os.path.join(Wa.BuildDir, TAB_FV_DIRECTORY) + self.VPDBaseAddress = 0 + self.VPDSize = 0 + for index, FdRegion in enumerate(Fd.RegionList): + if str(FdRegion.RegionType) == 'FILE' and Wa.Platform.VpdToolGuid in str(FdRegion.RegionDataList): + self.VPDBaseAddress = self.FdRegionList[index].BaseAddress + self.VPDSize = self.FdRegionList[index].Size + break + + ## + # 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) + + if VPDPcdList: + VPDPcdList.sort(key=lambda x: int(x[2], 0)) + FileWrite(File, gSubSectionStart) + FileWrite(File, "FD VPD Region") + FileWrite(File, "Base Address: 0x%X" % self.VPDBaseAddress) + FileWrite(File, "Size: 0x%X (%.0fK)" % (self.VPDSize, self.VPDSize / 1024.0)) + FileWrite(File, gSubSectionSep) + for item in VPDPcdList: + # Add BaseAddress for offset + Offset = '0x%08X' % (int(item[2], 16) + self.VPDBaseAddress) + IsByteArray, ArrayList = ByteArrayForamt(item[-1]) + Skuinfo = item[1] + if len(GlobalData.gSkuids) == 1 : + Skuinfo = GlobalData.gSkuids[0] + if IsByteArray: + FileWrite(File, "%s | %s | %s | %s | %s" % (item[0], Skuinfo, Offset, item[3], '{')) + for Array in ArrayList: + FileWrite(File, Array) + else: + FileWrite(File, "%s | %s | %s | %s | %s" % (item[0], Skuinfo, Offset, item[3], item[-1])) + FileWrite(File, gSubSectionEnd) + 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 + # @param MaList The list of modules in the platform build + # + def __init__(self, Wa, MaList, 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 and MaList is None: + 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.DepexParser = None + if "DEPEX" in ReportType: + self.DepexParser = DepexParser(Wa) + + self.ModuleReportList = [] + if MaList is not None: + self._IsModuleBuild = True + for Ma in MaList: + self.ModuleReportList.append(ModuleReport(Ma, ReportType)) + else: + self._IsModuleBuild = False + for Pa in Wa.AutoGenObjectList: + ModuleAutoGenList = [] + for ModuleKey in Pa.Platform.Modules: + ModuleAutoGenList.append(Pa.Platform.Modules[ModuleKey].M) + if GlobalData.gFdfParser is not None: + if Pa.Arch in GlobalData.gFdfParser.Profile.InfDict: + INFList = GlobalData.gFdfParser.Profile.InfDict[Pa.Arch] + for InfName in INFList: + InfClass = PathClass(NormPath(InfName), Wa.WorkspaceDir, Pa.Arch) + Ma = ModuleAutoGen(Wa, InfClass, Pa.BuildTarget, Pa.ToolChain, Pa.Arch, Wa.MetaFile, Pa.DataPipe) + if Ma is None: + continue + if Ma not in ModuleAutoGenList: + ModuleAutoGenList.append(Ma) + for MGen in ModuleAutoGenList: + self.ModuleReportList.append(ModuleReport(MGen, 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 AutoGenTime The total time of AutoGen Phase + # @param MakeTime The total time of Make Phase + # @param GenFdsTime The total time of GenFds Phase + # @param ReportType The kind of report items in the final report file + # + def GenerateReport(self, File, BuildDuration, AutoGenTime, MakeTime, GenFdsTime, 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) + if GlobalData.gSkuids: + FileWrite(File, "SKUID: %s" % " ".join(GlobalData.gSkuids)) + if GlobalData.gDefaultStores: + FileWrite(File, "DefaultStore: %s" % " ".join(GlobalData.gDefaultStores)) + FileWrite(File, "Output Path: %s" % self.OutputPath) + FileWrite(File, "Build Environment: %s" % self.BuildEnvironment) + FileWrite(File, "Build Duration: %s" % BuildDuration) + if AutoGenTime: + FileWrite(File, "AutoGen Duration: %s" % AutoGenTime) + if MakeTime: + FileWrite(File, "Make Duration: %s" % MakeTime) + if GenFdsTime: + FileWrite(File, "GenFds Duration: %s" % GenFdsTime) + FileWrite(File, "Report Content: %s" % ", ".join(ReportType)) + + if GlobalData.MixedPcd: + FileWrite(File, gSectionStart) + FileWrite(File, "The following PCDs use different access methods:") + FileWrite(File, gSectionSep) + for PcdItem in GlobalData.MixedPcd: + FileWrite(File, "%s.%s" % (str(PcdItem[1]), str(PcdItem[0]))) + FileWrite(File, gSectionEnd) + + if not self._IsModuleBuild: + 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, self.DepexParser, ReportType) + + if not self._IsModuleBuild: + 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", "HASH", "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 + # @param MaList The list of modules in the platform build + # + def AddPlatformReport(self, Wa, MaList=None): + if self.ReportFile: + self.ReportList.append((Wa, MaList)) + + ## + # 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 + # @param AutoGenTime The total time of AutoGen phase + # @param MakeTime The total time of Make phase + # @param GenFdsTime The total time of GenFds phase + # + def GenerateReport(self, BuildDuration, AutoGenTime, MakeTime, GenFdsTime): + if self.ReportFile: + try: + + if "COMPILE_INFO" in self.ReportType: + self.GenerateCompileInfo() + + File = [] + for (Wa, MaList) in self.ReportList: + PlatformReport(Wa, MaList, self.ReportType).GenerateReport(File, BuildDuration, AutoGenTime, MakeTime, GenFdsTime, self.ReportType) + Content = FileLinesSplit(''.join(File), gLineMaxLength) + SaveFileOnChange(self.ReportFile, Content, False) + EdkLogger.quiet("Build report can be found at %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())) + + + ## + # Generates compile data files to be used by external tools. + # Compile information will be generated in <Build>/<BuildTarget>/<ToolChain>/CompileInfo + # Files generated: compile_commands.json, cscope.files, modules_report.json + # + # @param self The object pointer + # + def GenerateCompileInfo(self): + try: + # Lists for the output elements + compile_commands = [] + used_files = set() + module_report = [] + + for (Wa, MaList) in self.ReportList: + # Obtain list of all processed Workspace files + for file_path in Wa._GetMetaFiles(Wa.BuildTarget, Wa.ToolChain): + used_files.add(file_path) + + for autoGen in Wa.AutoGenObjectList: + + # Loop through all modules + for module in (autoGen.LibraryAutoGenList + autoGen.ModuleAutoGenList): + + used_files.add(module.MetaFile.Path) + + # Main elements of module report + module_report_data = {} + module_report_data["Name"] = module.Name + module_report_data["Arch"] = module.Arch + module_report_data["Path"] = module.MetaFile.Path + module_report_data["Guid"] = module.Guid + module_report_data["BuildType"] = module.BuildType + module_report_data["IsLibrary"] = module.IsLibrary + module_report_data["SourceDir"] = module.SourceDir + module_report_data["Files"] = [] + module_report_data["LibraryClass"] = module.Module.LibraryClass + module_report_data["ModuleEntryPointList"] = module.Module.ModuleEntryPointList + module_report_data["ConstructorList"] = module.Module.ConstructorList + module_report_data["DestructorList"] = module.Module.DestructorList + + # Files used by module + for data_file in module.SourceFileList: + module_report_data["Files"].append({"Name": data_file.Name, "Path": data_file.Path}) + + # Libraries used by module + module_report_data["Libraries"] = [] + for data_library in module.LibraryAutoGenList: + module_report_data["Libraries"].append({"Path": data_library.MetaFile.Path}) + + # Packages used by module + module_report_data["Packages"] = [] + for data_package in module.PackageList: + module_report_data["Packages"].append({"Path": data_package.MetaFile.Path, "Includes": []}) + # Includes path used in package + for data_package_include in data_package.Includes: + module_report_data["Packages"][-1]["Includes"].append(data_package_include.Path) + + # PPI's in module + module_report_data["PPI"] = [] + for data_ppi in module.PpiList.keys(): + module_report_data["PPI"].append({"Name": data_ppi, "Guid": module.PpiList[data_ppi]}) + + # Protocol's in module + module_report_data["Protocol"] = [] + for data_protocol in module.ProtocolList.keys(): + module_report_data["Protocol"].append({"Name": data_protocol, "Guid": module.ProtocolList[data_protocol]}) + + # PCD's in module + module_report_data["Pcd"] = [] + for data_pcd in module.LibraryPcdList: + module_report_data["Pcd"].append({"Space": data_pcd.TokenSpaceGuidCName, + "Name": data_pcd.TokenCName, + "Value": data_pcd.TokenValue, + "Guid": data_pcd.TokenSpaceGuidValue, + "DatumType": data_pcd.DatumType, + "Type": data_pcd.Type, + "DefaultValue": data_pcd.DefaultValue}) + # Add module to report + module_report.append(module_report_data) + + # Include file dependencies to used files + includes_autogen = IncludesAutoGen(module.MakeFileDir, module) + for dep in includes_autogen.DepsCollection: + used_files.add(dep) + + inc_flag = "-I" # Default include flag + if module.BuildRuleFamily == TAB_COMPILER_MSFT: + inc_flag = "/I" + + for source in module.SourceFileList: + used_files.add(source.Path) + compile_command = {} + if source.Ext in [".c", ".cc", ".cpp"]: + # + # Generate compile command for each c file + # + compile_command["file"] = source.Path + compile_command["directory"] = source.Dir + build_command = module.BuildRules[source.Ext].CommandList[0] + build_command_variables = re.findall(r"\$\((.*?)\)", build_command) + for var in build_command_variables: + var_tokens = var.split("_") + var_main = var_tokens[0] + if len(var_tokens) == 1: + var_value = module.BuildOption[var_main]["PATH"] + else: + var_value = module.BuildOption[var_main][var_tokens[1]] + build_command = build_command.replace(f"$({var})", var_value) + include_files = f" {inc_flag}".join(module.IncludePathList) + build_command = build_command.replace("${src}", include_files) + build_command = build_command.replace("${dst}", module.OutputDir) + + # Remove un defined macros + compile_command["command"] = re.sub(r"\$\(.*?\)", "", build_command) + compile_commands.append(compile_command) + + # Create output folder if doesn't exist + compile_info_folder = Path(Wa.BuildDir).joinpath("CompileInfo") + compile_info_folder.mkdir(exist_ok=True) + + # Sort and save files + compile_commands.sort(key=lambda x: x["file"]) + SaveFileOnChange(compile_info_folder.joinpath(f"compile_commands.json"),json.dumps(compile_commands, indent=2), False) + + SaveFileOnChange(compile_info_folder.joinpath(f"cscope.files"), "\n".join(sorted(used_files)), False) + + module_report.sort(key=lambda x: x["Path"]) + SaveFileOnChange(compile_info_folder.joinpath(f"module_report.json"), json.dumps(module_report, indent=2), False) + + except: + EdkLogger.error("BuildReport", CODE_ERROR, "Unknown fatal error when generating build report compile information", ExtraData=self.ReportFile, RaiseError=False) + EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc())) + +# 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 41a3808ae9..76fba8f0b2 100644 --- a/BaseTools/Source/Python/build/__init__.py +++ b/BaseTools/Source/Python/build/__init__.py @@ -1,9 +1,9 @@ -## @file
-# Python 'build' package initialization file.
-#
-# This file is required to make Python interpreter treat the directory
-# as containing package.
-#
-# Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>
-# SPDX-License-Identifier: BSD-2-Clause-Patent
-#
+## @file +# Python 'build' package initialization file. +# +# This file is required to make Python interpreter treat the directory +# as containing package. +# +# Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# diff --git a/BaseTools/Source/Python/build/build.py b/BaseTools/Source/Python/build/build.py index 51fb1f433e..b0c02718c4 100755 --- a/BaseTools/Source/Python/build/build.py +++ b/BaseTools/Source/Python/build/build.py @@ -1,2789 +1,2789 @@ -## @file
-# build a platform or a module
-#
-# Copyright (c) 2014, Hewlett-Packard Development Company, L.P.<BR>
-# Copyright (c) 2007 - 2021, Intel Corporation. All rights reserved.<BR>
-# Copyright (c) 2018, Hewlett Packard Enterprise Development, L.P.<BR>
-# Copyright (c) 2020 - 2021, ARM Limited. All rights reserved.<BR>
-#
-# SPDX-License-Identifier: BSD-2-Clause-Patent
-#
-
-##
-# Import Modules
-#
-from __future__ import print_function
-from __future__ import absolute_import
-import os.path as path
-import sys
-import os
-import re
-import glob
-import time
-import platform
-import traceback
-import multiprocessing
-from threading import Thread,Event,BoundedSemaphore
-import threading
-from linecache import getlines
-from subprocess import Popen,PIPE, STDOUT
-from collections import OrderedDict, defaultdict
-
-from AutoGen.PlatformAutoGen import PlatformAutoGen
-from AutoGen.ModuleAutoGen import ModuleAutoGen
-from AutoGen.WorkspaceAutoGen import WorkspaceAutoGen
-from AutoGen.AutoGenWorker import AutoGenWorkerInProcess,AutoGenManager,\
- LogAgent
-from AutoGen import GenMake
-from Common import Misc as Utils
-
-from Common.TargetTxtClassObject import TargetTxtDict
-from Common.ToolDefClassObject import ToolDefDict
-from buildoptions import MyOptionParser
-from Common.Misc import PathClass,SaveFileOnChange,RemoveDirectory
-from Common.StringUtils import NormPath
-from Common.MultipleWorkspace import MultipleWorkspace as mws
-from Common.BuildToolError import *
-from Common.DataType import *
-import Common.EdkLogger as EdkLogger
-
-from Workspace.WorkspaceDatabase import BuildDB
-
-from BuildReport import BuildReport
-from GenPatchPcdTable.GenPatchPcdTable import PeImageClass,parsePcdInfoFromMapFile
-from PatchPcdValue.PatchPcdValue import PatchBinaryFile
-
-import Common.GlobalData as GlobalData
-from GenFds.GenFds import GenFds, GenFdsApi
-import multiprocessing as mp
-from multiprocessing import Manager
-from AutoGen.DataPipe import MemoryDataPipe
-from AutoGen.ModuleAutoGenHelper import WorkSpaceInfo, PlatformInfo
-from GenFds.FdfParser import FdfParser
-from AutoGen.IncludesAutoGen import IncludesAutoGen
-from GenFds.GenFds import resetFdsGlobalVariable
-from AutoGen.AutoGen import CalculatePriorityValue
-
-## standard targets of build command
-gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run']
-
-TemporaryTablePattern = re.compile(r'^_\d+_\d+_[a-fA-F0-9]+$')
-TmpTableDict = {}
-
-## 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 'PATHEXT' in os.environ:
- 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=WorkspaceDir)
- elif ' ' in WorkspaceDir:
- EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in WORKSPACE path",
- ExtraData=WorkspaceDir)
- os.environ["WORKSPACE"] = WorkspaceDir
-
- # set multiple workspace
- PackagesPath = os.getenv("PACKAGES_PATH")
- mws.setWs(WorkspaceDir, PackagesPath)
- if mws.PACKAGES_PATH:
- for Path in mws.PACKAGES_PATH:
- if not os.path.exists(Path):
- EdkLogger.error("build", FILE_NOT_FOUND, "One Path in PACKAGES_PATH doesn't exist", ExtraData=Path)
- elif ' ' in Path:
- EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in PACKAGES_PATH", ExtraData=Path)
-
-
- os.environ["EDK_TOOLS_PATH"] = os.path.normcase(os.environ["EDK_TOOLS_PATH"])
-
- # 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.gGlobalDefines["WORKSPACE"] = WorkspaceDir
- GlobalData.gGlobalDefines["EDK_TOOLS_PATH"] = os.environ["EDK_TOOLS_PATH"]
-
-## 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(mws.join(Workspace, FilePath))
- Workspace = mws.getWs(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,MemTo=None):
- while True:
- # read one line a time
- Line = From.readline()
- # empty string means "end"
- if Line is not None and Line != b"":
- LineStr = Line.rstrip().decode(encoding='utf-8', errors='ignore')
- if MemTo is not None:
- if "Note: including file:" == LineStr.lstrip()[:21]:
- MemTo.append(LineStr)
- else:
- To(LineStr)
- MemTo.append(LineStr)
- else:
- To(LineStr)
- else:
- break
- if ExitFlag.is_set():
- break
-
-class MakeSubProc(Popen):
- def __init__(self,*args, **argv):
- super(MakeSubProc,self).__init__(*args, **argv)
- self.ProcOut = []
-
-## 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 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,ModuleAuto = None):
- BeginTime = time.time()
- # 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)
-
- # Command is used as the first Argument in following Popen().
- # It could be a string or sequence. We find that if command is a string in following Popen(),
- # ubuntu may fail with an error message that the command is not found.
- # So here we may need convert command from string to list instance.
- if platform.system() != 'Windows':
- if not isinstance(Command, list):
- Command = Command.split()
- Command = ' '.join(Command)
-
- Proc = None
- EndOfProcedure = None
- try:
- # launch the command
- Proc = MakeSubProc(Command, stdout=PIPE, stderr=STDOUT, env=os.environ, cwd=WorkingDir, bufsize=-1, shell=True)
-
- # 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,Proc.ProcOut))
- StdOutThread.name = "STDOUT-Redirector"
- StdOutThread.daemon = False
- StdOutThread.start()
-
-
- # waiting for program exit
- Proc.wait()
- except: # in case of aborting
- # terminate the threads redirecting the program output
- EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
- if EndOfProcedure is not None:
- EndOfProcedure.set()
- if Proc is None:
- if not isinstance(Command, type("")):
- Command = " ".join(Command)
- EdkLogger.error("build", COMMAND_FAILURE, "Failed to start command", ExtraData="%s [%s]" % (Command, WorkingDir))
-
- if Proc.stdout:
- StdOutThread.join()
-
- # check the return code of the program
- if Proc.returncode != 0:
- if not isinstance(Command, type("")):
- Command = " ".join(Command)
- # print out the Response file and its content when make failure
- RespFile = os.path.join(WorkingDir, 'OUTPUT', 'respfilelist.txt')
- if os.path.isfile(RespFile):
- f = open(RespFile)
- RespContent = f.read()
- f.close()
- EdkLogger.info(RespContent)
-
- EdkLogger.error("build", COMMAND_FAILURE, ExtraData="%s [%s]" % (Command, WorkingDir))
- if ModuleAuto:
- iau = IncludesAutoGen(WorkingDir,ModuleAuto)
- if ModuleAuto.ToolChainFamily == TAB_COMPILER_MSFT:
- iau.CreateDepsFileForMsvc(Proc.ProcOut)
- else:
- iau.UpdateDepsFileforNonMsvc()
- iau.UpdateDepsFileforTrim()
- iau.CreateModuleDeps()
- iau.CreateDepsInclude()
- iau.CreateDepsTarget()
- return "%dms" % (int(round((time.time() - BeginTime) * 1000)))
-
-## 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 not BuildCommand:
- EdkLogger.error("build", OPTION_MISSING,
- "No build command found for this module. "
- "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %
- (Obj.BuildTarget, Obj.ToolChain, Obj.Arch),
- ExtraData=str(Obj))
-
-
- ## str() method
- #
- # It just returns the string representation 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 and self.BuildObject == Other.BuildObject \
- and 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, BuildCommand,Target):
- Dependency = [ModuleMakeUnit(La, BuildCommand,Target) for La in Obj.LibraryAutoGenList]
- BuildUnit.__init__(self, 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, BuildCommand, Target):
- Dependency = [ModuleMakeUnit(Lib, BuildCommand, Target) for Lib in self.BuildObject.LibraryAutoGenList]
- Dependency.extend([ModuleMakeUnit(Mod, BuildCommand,Target) for Mod in self.BuildObject.ModuleAutoGenList])
- BuildUnit.__init__(self, 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 = OrderedDict()
- _PendingQueueLock = threading.Lock()
-
- # queue for tasks ready for running
- _ReadyQueue = OrderedDict()
- _ReadyQueueLock = threading.Lock()
-
- # queue for run tasks
- _RunningQueue = OrderedDict()
- _RunningQueueLock = threading.Lock()
-
- # queue containing all build tasks, in case duplicate build
- _TaskQueue = OrderedDict()
-
- # 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.name = "Build-Task-Scheduler"
- SchedulerThread.daemon = 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.is_set()) and not BuildTask._ErrorFlag.is_set():
- 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 = list(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.is_set():
- # 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, Bt = BuildTask._ReadyQueue.popitem()
-
- # 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.is_set():
- EdkLogger.quiet("\nWaiting for all build threads exit...")
- # while not BuildTask._ErrorFlag.is_set() 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.name for Th in threading.enumerate()))
- # avoid tense loop
- time.sleep(0.1)
- except BaseException as X:
- #
- # TRICK: hide the output of threads left running, 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.is_set()
-
- ## 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.is_set()
-
- ## 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 is 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:
- if not Dep.BuildObject.IsBinaryModule and not Dep.BuildObject.CanSkipbyCache(GlobalData.gModuleCacheHit):
- 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:
- self.BuildItem.BuildObject.BuildTime = LaunchCommand(Command, WorkingDir,self.BuildItem.BuildObject)
- self.CompleteFlag = True
-
- # Run hash operation post dependency to account for libs
- # Run if --hash or --binary-destination
- if GlobalData.gUseHashCache and not GlobalData.gBinCacheSource:
- self.BuildItem.BuildObject.GenModuleHash()
- if GlobalData.gBinCacheDest:
- self.BuildItem.BuildObject.GenCMakeHash()
-
- except:
- #
- # TRICK: hide the output of threads left running, so that the user can
- # catch the error message easily
- #
- if not BuildTask._ErrorFlag.is_set():
- 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.current_thread().name, 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.name = "build thread"
- self.BuildTread.daemon = 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 OutputDir The output directory for image.
- # @param DebugDir The debug directory for image.
- # @param ImageClass PeImage Information
- #
- def __init__(self, BaseName, Guid, Arch, OutputDir, DebugDir, ImageClass):
- self.BaseName = BaseName
- self.Guid = Guid
- self.Arch = Arch
- self.OutputDir = OutputDir
- self.DebugDir = DebugDir
- 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 BuildOptions Build options passed from command line
- #
- def __init__(self, Target, WorkspaceDir, BuildOptions,log_q):
- self.WorkspaceDir = WorkspaceDir
- self.Target = Target
- self.PlatformFile = BuildOptions.PlatformFile
- self.ModuleFile = BuildOptions.ModuleFile
- self.ArchList = BuildOptions.TargetArch
- self.ToolChainList = BuildOptions.ToolChain
- self.BuildTargetList= BuildOptions.BuildTarget
- self.Fdf = BuildOptions.FdfFile
- self.FdList = BuildOptions.RomImage
- self.FvList = BuildOptions.FvImage
- self.CapList = BuildOptions.CapName
- self.SilentMode = BuildOptions.SilentMode
- self.ThreadNumber = 1
- self.SkipAutoGen = BuildOptions.SkipAutoGen
- self.Reparse = BuildOptions.Reparse
- self.SkuId = BuildOptions.SkuId
- if self.SkuId:
- GlobalData.gSKUID_CMD = self.SkuId
- self.ConfDirectory = BuildOptions.ConfDirectory
- self.SpawnMode = True
- self.BuildReport = BuildReport(BuildOptions.ReportFile, BuildOptions.ReportType)
- self.AutoGenTime = 0
- self.MakeTime = 0
- self.GenFdsTime = 0
- self.MakeFileName = ""
- TargetObj = TargetTxtDict()
- ToolDefObj = ToolDefDict((os.path.join(os.getenv("WORKSPACE"),"Conf")))
- self.TargetTxt = TargetObj.Target
- self.ToolDef = ToolDefObj.ToolDef
- GlobalData.BuildOptionPcd = BuildOptions.OptionPcd if BuildOptions.OptionPcd else []
- #Set global flag for build mode
- GlobalData.gIgnoreSource = BuildOptions.IgnoreSources
- GlobalData.gUseHashCache = BuildOptions.UseHashCache
- GlobalData.gBinCacheDest = BuildOptions.BinCacheDest
- GlobalData.gBinCacheSource = BuildOptions.BinCacheSource
- GlobalData.gEnableGenfdsMultiThread = not BuildOptions.NoGenfdsMultiThread
- GlobalData.gDisableIncludePathCheck = BuildOptions.DisableIncludePathCheck
-
- if GlobalData.gBinCacheDest and not GlobalData.gUseHashCache:
- EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-destination must be used together with --hash.")
-
- if GlobalData.gBinCacheSource and not GlobalData.gUseHashCache:
- EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-source must be used together with --hash.")
-
- if GlobalData.gBinCacheDest and GlobalData.gBinCacheSource:
- EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-destination can not be used together with --binary-source.")
-
- if GlobalData.gBinCacheSource:
- BinCacheSource = os.path.normpath(GlobalData.gBinCacheSource)
- if not os.path.isabs(BinCacheSource):
- BinCacheSource = mws.join(self.WorkspaceDir, BinCacheSource)
- GlobalData.gBinCacheSource = BinCacheSource
- else:
- if GlobalData.gBinCacheSource is not None:
- EdkLogger.error("build", OPTION_VALUE_INVALID, ExtraData="Invalid value of option --binary-source.")
-
- if GlobalData.gBinCacheDest:
- BinCacheDest = os.path.normpath(GlobalData.gBinCacheDest)
- if not os.path.isabs(BinCacheDest):
- BinCacheDest = mws.join(self.WorkspaceDir, BinCacheDest)
- GlobalData.gBinCacheDest = BinCacheDest
- else:
- if GlobalData.gBinCacheDest is not None:
- EdkLogger.error("build", OPTION_VALUE_INVALID, ExtraData="Invalid value of option --binary-destination.")
-
- GlobalData.gDatabasePath = os.path.normpath(os.path.join(GlobalData.gConfDirectory, GlobalData.gDatabasePath))
- if not os.path.exists(os.path.join(GlobalData.gConfDirectory, '.cache')):
- os.makedirs(os.path.join(GlobalData.gConfDirectory, '.cache'))
- self.Db = BuildDB
- self.BuildDatabase = self.Db.BuildObject
- self.Platform = None
- self.ToolChainFamily = None
- self.LoadFixAddress = 0
- self.UniFlag = BuildOptions.Flag
- self.BuildModules = []
- self.HashSkipModules = []
- self.Db_Flag = False
- self.LaunchPrebuildFlag = False
- self.PlatformBuildPath = os.path.join(GlobalData.gConfDirectory, '.cache', '.PlatformBuild')
- if BuildOptions.CommandLength:
- GlobalData.gCommandMaxLength = BuildOptions.CommandLength
-
- # print dot character during doing some time-consuming work
- self.Progress = Utils.Progressor()
- # print current build environment and configuration
- EdkLogger.quiet("%-16s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))
- if "PACKAGES_PATH" in os.environ:
- # WORKSPACE env has been converted before. Print the same path style with WORKSPACE env.
- EdkLogger.quiet("%-16s = %s" % ("PACKAGES_PATH", os.path.normcase(os.path.normpath(os.environ["PACKAGES_PATH"]))))
- EdkLogger.quiet("%-16s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))
- if "EDK_TOOLS_BIN" in os.environ:
- # Print the same path style with WORKSPACE env.
- EdkLogger.quiet("%-16s = %s" % ("EDK_TOOLS_BIN", os.path.normcase(os.path.normpath(os.environ["EDK_TOOLS_BIN"]))))
- EdkLogger.quiet("%-16s = %s" % ("CONF_PATH", GlobalData.gConfDirectory))
- if "PYTHON3_ENABLE" in os.environ:
- PYTHON3_ENABLE = os.environ["PYTHON3_ENABLE"]
- if PYTHON3_ENABLE != "TRUE":
- PYTHON3_ENABLE = "FALSE"
- EdkLogger.quiet("%-16s = %s" % ("PYTHON3_ENABLE", PYTHON3_ENABLE))
- if "PYTHON_COMMAND" in os.environ:
- EdkLogger.quiet("%-16s = %s" % ("PYTHON_COMMAND", os.environ["PYTHON_COMMAND"]))
- self.InitPreBuild()
- self.InitPostBuild()
- if self.Prebuild:
- EdkLogger.quiet("%-16s = %s" % ("PREBUILD", self.Prebuild))
- if self.Postbuild:
- EdkLogger.quiet("%-16s = %s" % ("POSTBUILD", self.Postbuild))
- if self.Prebuild:
- self.LaunchPrebuild()
- TargetObj = TargetTxtDict()
- ToolDefObj = ToolDefDict((os.path.join(os.getenv("WORKSPACE"), "Conf")))
- self.TargetTxt = TargetObj.Target
- self.ToolDef = ToolDefObj.ToolDef
- if not (self.LaunchPrebuildFlag and os.path.exists(self.PlatformBuildPath)):
- self.InitBuild()
-
- self.AutoGenMgr = None
- EdkLogger.info("")
- os.chdir(self.WorkspaceDir)
- self.log_q = log_q
- GlobalData.file_lock = mp.Lock()
- # Init cache data for local only
- GlobalData.gPackageHashFile = dict()
- GlobalData.gModulePreMakeCacheStatus = dict()
- GlobalData.gModuleMakeCacheStatus = dict()
- GlobalData.gHashChainStatus = dict()
- GlobalData.gCMakeHashFile = dict()
- GlobalData.gModuleHashFile = dict()
- GlobalData.gFileHashDict = dict()
- GlobalData.gModuleAllCacheStatus = set()
- GlobalData.gModuleCacheHit = set()
-
- def StartAutoGen(self,mqueue, DataPipe,SkipAutoGen,PcdMaList,cqueue):
- try:
- if SkipAutoGen:
- return True,0
- feedback_q = mp.Queue()
- error_event = mp.Event()
- FfsCmd = DataPipe.Get("FfsCommand")
- if FfsCmd is None:
- FfsCmd = {}
- GlobalData.FfsCmd = FfsCmd
- auto_workers = [AutoGenWorkerInProcess(mqueue,DataPipe.dump_file,feedback_q,GlobalData.file_lock,cqueue,self.log_q,error_event) for _ in range(self.ThreadNumber)]
- self.AutoGenMgr = AutoGenManager(auto_workers,feedback_q,error_event)
- self.AutoGenMgr.start()
- for w in auto_workers:
- w.start()
- if PcdMaList is not None:
- for PcdMa in PcdMaList:
- # SourceFileList calling sequence impact the makefile string sequence.
- # Create cached SourceFileList here to unify its calling sequence for both
- # CanSkipbyPreMakeCache and CreateCodeFile/CreateMakeFile.
- RetVal = PcdMa.SourceFileList
- # Force cache miss for PCD driver
- if GlobalData.gUseHashCache and not GlobalData.gBinCacheDest and self.Target in [None, "", "all"]:
- cqueue.put((PcdMa.MetaFile.Path, PcdMa.Arch, "PreMakeCache", False))
-
- PcdMa.CreateCodeFile(False)
- PcdMa.CreateMakeFile(False,GenFfsList = DataPipe.Get("FfsCommand").get((PcdMa.MetaFile.Path, PcdMa.Arch),[]))
- PcdMa.CreateAsBuiltInf()
- # Force cache miss for PCD driver
- if GlobalData.gBinCacheSource and self.Target in [None, "", "all"]:
- cqueue.put((PcdMa.MetaFile.Path, PcdMa.Arch, "MakeCache", False))
-
- self.AutoGenMgr.join()
- rt = self.AutoGenMgr.Status
- err = 0
- if not rt:
- err = UNKNOWN_ERROR
- return rt, err
- except FatalError as e:
- return False, e.args[0]
- except:
- return False, UNKNOWN_ERROR
-
- ## Add TOOLCHAIN and FAMILY declared in DSC [BuildOptions] to ToolsDefTxtDatabase.
- #
- # Loop through the set of build targets, tool chains, and archs provided on either
- # the command line or in target.txt to discover FAMILY and TOOLCHAIN delclarations
- # in [BuildOptions] sections that may be within !if expressions that may use
- # $(TARGET), $(TOOLCHAIN), $(TOOLCHAIN_TAG), or $(ARCH) operands.
- #
- def GetToolChainAndFamilyFromDsc (self, File):
- SavedGlobalDefines = GlobalData.gGlobalDefines.copy()
- for BuildTarget in self.BuildTargetList:
- GlobalData.gGlobalDefines['TARGET'] = BuildTarget
- for BuildToolChain in self.ToolChainList:
- GlobalData.gGlobalDefines['TOOLCHAIN'] = BuildToolChain
- GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = BuildToolChain
- for BuildArch in self.ArchList:
- GlobalData.gGlobalDefines['ARCH'] = BuildArch
- dscobj = self.BuildDatabase[File, BuildArch]
- for KeyFamily, Key, KeyCodeBase in dscobj.BuildOptions:
- try:
- Target, ToolChain, Arch, Tool, Attr = Key.split('_')
- except:
- continue
- if ToolChain == TAB_STAR or Attr != TAB_TOD_DEFINES_FAMILY:
- continue
- try:
- Family = dscobj.BuildOptions[(KeyFamily, Key, KeyCodeBase)]
- Family = Family.strip().strip('=').strip()
- except:
- continue
- if TAB_TOD_DEFINES_FAMILY not in self.ToolDef.ToolsDefTxtDatabase:
- self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_FAMILY] = {}
- if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_FAMILY]:
- self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_FAMILY][ToolChain] = Family
- if TAB_TOD_DEFINES_BUILDRULEFAMILY not in self.ToolDef.ToolsDefTxtDatabase:
- self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_BUILDRULEFAMILY] = {}
- if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_BUILDRULEFAMILY]:
- self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_BUILDRULEFAMILY][ToolChain] = Family
- if TAB_TOD_DEFINES_TOOL_CHAIN_TAG not in self.ToolDef.ToolsDefTxtDatabase:
- self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG] = []
- if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:
- self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG].append(ToolChain)
- GlobalData.gGlobalDefines = SavedGlobalDefines
-
- ## Load configuration
- #
- # This method will parse target.txt and get the build configurations.
- #
- def LoadConfiguration(self):
-
- # if no ARCH given in command line, get it from target.txt
- if not self.ArchList:
- self.ArchList = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TARGET_ARCH]
- self.ArchList = tuple(self.ArchList)
-
- # if no build target given in command line, get it from target.txt
- if not self.BuildTargetList:
- self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TARGET]
-
- # if no tool chain given in command line, get it from target.txt
- if not self.ToolChainList:
- self.ToolChainList = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TOOL_CHAIN_TAG]
- if self.ToolChainList is None or len(self.ToolChainList) == 0:
- EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")
-
- if not self.PlatformFile:
- PlatformFile = self.TargetTxt.TargetTxtDictionary[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)
-
- self.GetToolChainAndFamilyFromDsc (self.PlatformFile)
-
- # 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
-
- ToolChainFamily = []
- ToolDefinition = self.ToolDef.ToolsDefTxtDatabase
- for Tool in self.ToolChainList:
- if TAB_TOD_DEFINES_FAMILY not in ToolDefinition or Tool not in ToolDefinition[TAB_TOD_DEFINES_FAMILY] \
- or not ToolDefinition[TAB_TOD_DEFINES_FAMILY][Tool]:
- EdkLogger.warn("build", "No tool chain family found in configuration for %s. Default to MSFT." % Tool)
- ToolChainFamily.append(TAB_COMPILER_MSFT)
- else:
- ToolChainFamily.append(ToolDefinition[TAB_TOD_DEFINES_FAMILY][Tool])
- self.ToolChainFamily = ToolChainFamily
-
- self.ThreadNumber = ThreadNum()
- ## 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):
- # parse target.txt, tools_def.txt, and platform file
- self.LoadConfiguration()
-
- # Allow case-insensitive for those from command line or configuration file
- ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)
- if ErrorCode != 0:
- EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
-
-
- def InitPreBuild(self):
- self.LoadConfiguration()
- ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)
- if ErrorCode != 0:
- EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
- if self.BuildTargetList:
- GlobalData.gGlobalDefines['TARGET'] = self.BuildTargetList[0]
- if self.ArchList:
- GlobalData.gGlobalDefines['ARCH'] = self.ArchList[0]
- if self.ToolChainList:
- GlobalData.gGlobalDefines['TOOLCHAIN'] = self.ToolChainList[0]
- GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = self.ToolChainList[0]
- if self.ToolChainFamily:
- GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[0]
- if 'PREBUILD' in GlobalData.gCommandLineDefines:
- self.Prebuild = GlobalData.gCommandLineDefines.get('PREBUILD')
- else:
- self.Db_Flag = True
- Platform = self.Db.MapPlatform(str(self.PlatformFile))
- self.Prebuild = str(Platform.Prebuild)
- if self.Prebuild:
- PrebuildList = []
- #
- # Evaluate all arguments and convert arguments that are WORKSPACE
- # relative paths to absolute paths. Filter arguments that look like
- # flags or do not follow the file/dir naming rules to avoid false
- # positives on this conversion.
- #
- for Arg in self.Prebuild.split():
- #
- # Do not modify Arg if it looks like a flag or an absolute file path
- #
- if Arg.startswith('-') or os.path.isabs(Arg):
- PrebuildList.append(Arg)
- continue
- #
- # Do not modify Arg if it does not look like a Workspace relative
- # path that starts with a valid package directory name
- #
- if not Arg[0].isalpha() or os.path.dirname(Arg) == '':
- PrebuildList.append(Arg)
- continue
- #
- # If Arg looks like a WORKSPACE relative path, then convert to an
- # absolute path and check to see if the file exists.
- #
- Temp = mws.join(self.WorkspaceDir, Arg)
- if os.path.isfile(Temp):
- Arg = Temp
- PrebuildList.append(Arg)
- self.Prebuild = ' '.join(PrebuildList)
- self.Prebuild += self.PassCommandOption(self.BuildTargetList, self.ArchList, self.ToolChainList, self.PlatformFile, self.Target)
-
- def InitPostBuild(self):
- if 'POSTBUILD' in GlobalData.gCommandLineDefines:
- self.Postbuild = GlobalData.gCommandLineDefines.get('POSTBUILD')
- else:
- Platform = self.Db.MapPlatform(str(self.PlatformFile))
- self.Postbuild = str(Platform.Postbuild)
- if self.Postbuild:
- PostbuildList = []
- #
- # Evaluate all arguments and convert arguments that are WORKSPACE
- # relative paths to absolute paths. Filter arguments that look like
- # flags or do not follow the file/dir naming rules to avoid false
- # positives on this conversion.
- #
- for Arg in self.Postbuild.split():
- #
- # Do not modify Arg if it looks like a flag or an absolute file path
- #
- if Arg.startswith('-') or os.path.isabs(Arg):
- PostbuildList.append(Arg)
- continue
- #
- # Do not modify Arg if it does not look like a Workspace relative
- # path that starts with a valid package directory name
- #
- if not Arg[0].isalpha() or os.path.dirname(Arg) == '':
- PostbuildList.append(Arg)
- continue
- #
- # If Arg looks like a WORKSPACE relative path, then convert to an
- # absolute path and check to see if the file exists.
- #
- Temp = mws.join(self.WorkspaceDir, Arg)
- if os.path.isfile(Temp):
- Arg = Temp
- PostbuildList.append(Arg)
- self.Postbuild = ' '.join(PostbuildList)
- self.Postbuild += self.PassCommandOption(self.BuildTargetList, self.ArchList, self.ToolChainList, self.PlatformFile, self.Target)
-
- def PassCommandOption(self, BuildTarget, TargetArch, ToolChain, PlatformFile, Target):
- BuildStr = ''
- if GlobalData.gCommand and isinstance(GlobalData.gCommand, list):
- BuildStr += ' ' + ' '.join(GlobalData.gCommand)
- TargetFlag = False
- ArchFlag = False
- ToolChainFlag = False
- PlatformFileFlag = False
-
- if GlobalData.gOptions and not GlobalData.gOptions.BuildTarget:
- TargetFlag = True
- if GlobalData.gOptions and not GlobalData.gOptions.TargetArch:
- ArchFlag = True
- if GlobalData.gOptions and not GlobalData.gOptions.ToolChain:
- ToolChainFlag = True
- if GlobalData.gOptions and not GlobalData.gOptions.PlatformFile:
- PlatformFileFlag = True
-
- if TargetFlag and BuildTarget:
- if isinstance(BuildTarget, list) or isinstance(BuildTarget, tuple):
- BuildStr += ' -b ' + ' -b '.join(BuildTarget)
- elif isinstance(BuildTarget, str):
- BuildStr += ' -b ' + BuildTarget
- if ArchFlag and TargetArch:
- if isinstance(TargetArch, list) or isinstance(TargetArch, tuple):
- BuildStr += ' -a ' + ' -a '.join(TargetArch)
- elif isinstance(TargetArch, str):
- BuildStr += ' -a ' + TargetArch
- if ToolChainFlag and ToolChain:
- if isinstance(ToolChain, list) or isinstance(ToolChain, tuple):
- BuildStr += ' -t ' + ' -t '.join(ToolChain)
- elif isinstance(ToolChain, str):
- BuildStr += ' -t ' + ToolChain
- if PlatformFileFlag and PlatformFile:
- if isinstance(PlatformFile, list) or isinstance(PlatformFile, tuple):
- BuildStr += ' -p ' + ' -p '.join(PlatformFile)
- elif isinstance(PlatformFile, str):
- BuildStr += ' -p' + PlatformFile
- BuildStr += ' --conf=' + GlobalData.gConfDirectory
- if Target:
- BuildStr += ' ' + Target
-
- return BuildStr
-
- def LaunchPrebuild(self):
- if self.Prebuild:
- EdkLogger.info("\n- Prebuild Start -\n")
- self.LaunchPrebuildFlag = True
- #
- # The purpose of .PrebuildEnv file is capture environment variable settings set by the prebuild script
- # and preserve them for the rest of the main build step, because the child process environment will
- # evaporate as soon as it exits, we cannot get it in build step.
- #
- PrebuildEnvFile = os.path.join(GlobalData.gConfDirectory, '.cache', '.PrebuildEnv')
- if os.path.isfile(PrebuildEnvFile):
- os.remove(PrebuildEnvFile)
- if os.path.isfile(self.PlatformBuildPath):
- os.remove(self.PlatformBuildPath)
- if sys.platform == "win32":
- args = ' && '.join((self.Prebuild, 'set > ' + PrebuildEnvFile))
- Process = Popen(args, stdout=PIPE, stderr=PIPE, shell=True)
- else:
- args = ' && '.join((self.Prebuild, 'env > ' + PrebuildEnvFile))
- Process = Popen(args, stdout=PIPE, stderr=PIPE, shell=True)
-
- # launch two threads to read the STDOUT and STDERR
- EndOfProcedure = Event()
- EndOfProcedure.clear()
- if Process.stdout:
- StdOutThread = Thread(target=ReadMessage, args=(Process.stdout, EdkLogger.info, EndOfProcedure))
- StdOutThread.name = "STDOUT-Redirector"
- StdOutThread.daemon = False
- StdOutThread.start()
-
- if Process.stderr:
- StdErrThread = Thread(target=ReadMessage, args=(Process.stderr, EdkLogger.quiet, EndOfProcedure))
- StdErrThread.name = "STDERR-Redirector"
- StdErrThread.daemon = False
- StdErrThread.start()
- # waiting for program exit
- Process.wait()
-
- if Process.stdout:
- StdOutThread.join()
- if Process.stderr:
- StdErrThread.join()
- if Process.returncode != 0 :
- EdkLogger.error("Prebuild", PREBUILD_ERROR, 'Prebuild process is not success!')
-
- if os.path.exists(PrebuildEnvFile):
- f = open(PrebuildEnvFile)
- envs = f.readlines()
- f.close()
- envs = [l.split("=", 1) for l in envs ]
- envs = [[I.strip() for I in item] for item in envs if len(item) == 2]
- os.environ.update(dict(envs))
- EdkLogger.info("\n- Prebuild Done -\n")
-
- def LaunchPostbuild(self):
- if self.Postbuild:
- EdkLogger.info("\n- Postbuild Start -\n")
- if sys.platform == "win32":
- Process = Popen(self.Postbuild, stdout=PIPE, stderr=PIPE, shell=True)
- else:
- Process = Popen(self.Postbuild, stdout=PIPE, stderr=PIPE, shell=True)
- # launch two threads to read the STDOUT and STDERR
- EndOfProcedure = Event()
- EndOfProcedure.clear()
- if Process.stdout:
- StdOutThread = Thread(target=ReadMessage, args=(Process.stdout, EdkLogger.info, EndOfProcedure))
- StdOutThread.name = "STDOUT-Redirector"
- StdOutThread.daemon = False
- StdOutThread.start()
-
- if Process.stderr:
- StdErrThread = Thread(target=ReadMessage, args=(Process.stderr, EdkLogger.quiet, EndOfProcedure))
- StdErrThread.name = "STDERR-Redirector"
- StdErrThread.daemon = False
- StdErrThread.start()
- # waiting for program exit
- Process.wait()
-
- if Process.stdout:
- StdOutThread.join()
- if Process.stderr:
- StdErrThread.join()
- if Process.returncode != 0 :
- EdkLogger.error("Postbuild", POSTBUILD_ERROR, 'Postbuild process is not success!')
- EdkLogger.info("\n- Postbuild Done -\n")
-
- ## Build a module or platform
- #
- # Create autogen code and makefile 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 _BuildPa(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True, BuildModule=False, FfsCommand=None, PcdMaList=None):
- if AutoGenObject is None:
- return False
- if FfsCommand is None:
- FfsCommand = {}
- # 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
- mqueue = mp.Queue()
- for m in AutoGenObject.GetAllModuleInfo:
- mqueue.put(m)
- mqueue.put((None,None,None,None,None,None,None))
- AutoGenObject.DataPipe.DataContainer = {"CommandTarget": self.Target}
- AutoGenObject.DataPipe.DataContainer = {"Workspace_timestamp": AutoGenObject.Workspace._SrcTimeStamp}
- AutoGenObject.CreateLibModuelDirs()
- AutoGenObject.DataPipe.DataContainer = {"LibraryBuildDirectoryList":AutoGenObject.LibraryBuildDirectoryList}
- AutoGenObject.DataPipe.DataContainer = {"ModuleBuildDirectoryList":AutoGenObject.ModuleBuildDirectoryList}
- AutoGenObject.DataPipe.DataContainer = {"FdsCommandDict": AutoGenObject.Workspace.GenFdsCommandDict}
- self.Progress.Start("Generating makefile and code")
- data_pipe_file = os.path.join(AutoGenObject.BuildDir, "GlobalVar_%s_%s.bin" % (str(AutoGenObject.Guid),AutoGenObject.Arch))
- AutoGenObject.DataPipe.dump(data_pipe_file)
- cqueue = mp.Queue()
- autogen_rt,errorcode = self.StartAutoGen(mqueue, AutoGenObject.DataPipe, self.SkipAutoGen, PcdMaList, cqueue)
- AutoGenIdFile = os.path.join(GlobalData.gConfDirectory,".AutoGenIdFile.txt")
- with open(AutoGenIdFile,"w") as fw:
- fw.write("Arch=%s\n" % "|".join((AutoGenObject.Workspace.ArchList)))
- fw.write("BuildDir=%s\n" % AutoGenObject.Workspace.BuildDir)
- fw.write("PlatformGuid=%s\n" % str(AutoGenObject.Guid))
- self.Progress.Stop("done!")
- if not autogen_rt:
- self.AutoGenMgr.TerminateWorkers()
- self.AutoGenMgr.join(1)
- raise FatalError(errorcode)
- AutoGenObject.CreateCodeFile(False)
- AutoGenObject.CreateMakeFile(False)
- else:
- # always recreate top/platform makefile when clean, just in case of inconsistency
- AutoGenObject.CreateCodeFile(True)
- AutoGenObject.CreateMakeFile(True)
-
- if EdkLogger.GetLevel() == EdkLogger.QUIET:
- EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))
-
- BuildCommand = AutoGenObject.BuildCommand
- if BuildCommand is None or len(BuildCommand) == 0:
- EdkLogger.error("build", OPTION_MISSING,
- "No build command found for this module. "
- "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %
- (AutoGenObject.BuildTarget, AutoGenObject.ToolChain, AutoGenObject.Arch),
- ExtraData=str(AutoGenObject))
-
- # run
- if Target == 'run':
- return True
-
- # Fetch the MakeFileName.
- self.MakeFileName = AutoGenObject.MakeFileName
-
- # build modules
- if BuildModule:
- BuildCommand = BuildCommand + [Target]
- LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)
- if GlobalData.gBinCacheDest:
- self.GenDestCache()
- elif GlobalData.gUseHashCache and not GlobalData.gBinCacheSource:
- # Only for --hash
- # Update PreMakeCacheChain files
- self.GenLocalPreMakeCache()
- self.BuildModules = []
- return True
-
- # build library
- if Target == 'libraries':
- DirList = []
- for Lib in AutoGenObject.LibraryAutoGenList:
- if not Lib.IsBinaryModule:
- DirList.append((os.path.join(AutoGenObject.BuildDir, Lib.BuildDir),Lib))
- for Lib, LibAutoGen in DirList:
- NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Lib, self.MakeFileName)), 'pbuild']
- LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir,LibAutoGen)
- return True
-
- # build module
- if Target == 'modules':
- DirList = []
- for Lib in AutoGenObject.LibraryAutoGenList:
- if not Lib.IsBinaryModule:
- DirList.append((os.path.join(AutoGenObject.BuildDir, Lib.BuildDir),Lib))
- for Lib, LibAutoGen in DirList:
- NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Lib, self.MakeFileName)), 'pbuild']
- LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir,LibAutoGen)
-
- DirList = []
- for ModuleAutoGen in AutoGenObject.ModuleAutoGenList:
- if not ModuleAutoGen.IsBinaryModule:
- DirList.append((os.path.join(AutoGenObject.BuildDir, ModuleAutoGen.BuildDir),ModuleAutoGen))
- for Mod,ModAutoGen in DirList:
- NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Mod, self.MakeFileName)), 'pbuild']
- LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir,ModAutoGen)
- self.CreateAsBuiltInf()
- if GlobalData.gBinCacheDest:
- self.GenDestCache()
- elif GlobalData.gUseHashCache and not GlobalData.gBinCacheSource:
- # Only for --hash
- # Update PreMakeCacheChain files
- self.GenLocalPreMakeCache()
- self.BuildModules = []
- return True
-
- # cleanlib
- if Target == 'cleanlib':
- for Lib in AutoGenObject.LibraryBuildDirectoryList:
- LibMakefile = os.path.normpath(os.path.join(Lib, self.MakeFileName))
- if os.path.exists(LibMakefile):
- NewBuildCommand = BuildCommand + ['-f', LibMakefile, 'cleanall']
- LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)
- return True
-
- # clean
- if Target == 'clean':
- for Mod in AutoGenObject.ModuleBuildDirectoryList:
- ModMakefile = os.path.normpath(os.path.join(Mod, self.MakeFileName))
- if os.path.exists(ModMakefile):
- NewBuildCommand = BuildCommand + ['-f', ModMakefile, 'cleanall']
- LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)
- for Lib in AutoGenObject.LibraryBuildDirectoryList:
- LibMakefile = os.path.normpath(os.path.join(Lib, self.MakeFileName))
- if os.path.exists(LibMakefile):
- NewBuildCommand = BuildCommand + ['-f', LibMakefile, 'cleanall']
- LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)
- return True
-
- # cleanall
- if Target == 'cleanall':
- try:
- #os.rmdir(AutoGenObject.BuildDir)
- RemoveDirectory(AutoGenObject.BuildDir, True)
- except WindowsError as X:
- EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))
- return True
-
- ## Build a module or platform
- #
- # Create autogen code and makefile 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, BuildModule=False):
- if AutoGenObject is 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)
- #AutoGenObject.CreateAsBuiltInf()
- self.Progress.Stop("done!")
- if Target == "genmake":
- return True
- else:
- # always recreate top/platform makefile when clean, just in case of inconsistency
- AutoGenObject.CreateCodeFile(True)
- AutoGenObject.CreateMakeFile(True)
-
- if EdkLogger.GetLevel() == EdkLogger.QUIET:
- EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))
-
- BuildCommand = AutoGenObject.BuildCommand
- if BuildCommand is None or len(BuildCommand) == 0:
- EdkLogger.error("build", OPTION_MISSING,
- "No build command found for this module. "
- "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %
- (AutoGenObject.BuildTarget, AutoGenObject.ToolChain, AutoGenObject.Arch),
- ExtraData=str(AutoGenObject))
-
- # build modules
- if BuildModule:
- if Target != 'fds':
- BuildCommand = BuildCommand + [Target]
- AutoGenObject.BuildTime = LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)
- self.CreateAsBuiltInf()
- if GlobalData.gBinCacheDest:
- self.GenDestCache()
- elif GlobalData.gUseHashCache and not GlobalData.gBinCacheSource:
- # Only for --hash
- # Update PreMakeCacheChain files
- self.GenLocalPreMakeCache()
- self.BuildModules = []
- return True
-
- # genfds
- if Target == 'fds':
- if GenFdsApi(AutoGenObject.GenFdsCommandDict, self.Db):
- EdkLogger.error("build", COMMAND_FAILURE)
- Threshold = self.GetFreeSizeThreshold()
- if Threshold:
- self.CheckFreeSizeThreshold(Threshold, AutoGenObject.FvDir)
- return True
-
- # run
- if Target == 'run':
- return True
-
- # build library
- if Target == 'libraries':
- pass
-
- # not build modules
-
-
- # cleanall
- if Target == 'cleanall':
- try:
- #os.rmdir(AutoGenObject.BuildDir)
- RemoveDirectory(AutoGenObject.BuildDir, True)
- except WindowsError as X:
- EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))
- return True
-
- ## Rebase module image and Get function address for the input module list.
- #
- def _RebaseModule (self, MapBuffer, BaseAddress, ModuleList, AddrIsOffset = True, ModeIsSmm = False):
- if ModeIsSmm:
- AddrIsOffset = False
- for InfFile in ModuleList:
- sys.stdout.write (".")
- sys.stdout.flush()
- ModuleInfo = ModuleList[InfFile]
- ModuleName = ModuleInfo.BaseName
- ModuleOutputImage = ModuleInfo.Image.FileName
- ModuleDebugImage = os.path.join(ModuleInfo.DebugDir, ModuleInfo.BaseName + '.efi')
- ## 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", ModuleOutputImage], ModuleInfo.OutputDir)
- LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir)
- else:
- #
- # Set new address to the section header only for SMM driver.
- #
- LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleOutputImage], ModuleInfo.OutputDir)
- LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir)
- #
- # Collect function address from Map file
- #
- ImageMapTable = ModuleOutputImage.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))
-
- ImageMap.close()
- #
- # Add general information.
- #
- if ModeIsSmm:
- MapBuffer.append('\n\n%s (Fixed SMRAM Offset, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))
- elif AddrIsOffset:
- MapBuffer.append('\n\n%s (Fixed Memory Offset, BaseAddress=-0x%010X, EntryPoint=-0x%010X)\n' % (ModuleName, 0 - BaseAddress, 0 - (BaseAddress + ModuleInfo.Image.EntryPoint)))
- else:
- MapBuffer.append('\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.append('(GUID=%s, .textbaseaddress=-0x%010X, .databaseaddress=-0x%010X)\n' % (ModuleInfo.Guid, 0 - (BaseAddress + TextSectionAddress), 0 - (BaseAddress + DataSectionAddress)))
- else:
- MapBuffer.append('(GUID=%s, .textbaseaddress=0x%010X, .databaseaddress=0x%010X)\n' % (ModuleInfo.Guid, BaseAddress + TextSectionAddress, BaseAddress + DataSectionAddress))
- #
- # Add debug image full path.
- #
- MapBuffer.append('(IMAGE=%s)\n\n' % (ModuleDebugImage))
- #
- # Add function address
- #
- for Function in FunctionList:
- if AddrIsOffset:
- MapBuffer.append(' -0x%010X %s\n' % (0 - (BaseAddress + Function[1]), Function[0]))
- else:
- MapBuffer.append(' 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, ModuleList):
- if self.Fdf:
- # First get the XIP base address for FV map file.
- GuidPattern = re.compile("[-a-fA-F0-9]+")
- GuidName = re.compile(r"\(GUID=[-a-fA-F0-9]+")
- for FvName in Wa.FdfProfile.FvDict:
- 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()
- for Line in FvMap:
- MatchGuid = GuidPattern.match(Line)
- if MatchGuid is not None:
- #
- # Replace GUID with module name
- #
- GuidString = MatchGuid.group()
- if GuidString.upper() in ModuleList:
- Line = Line.replace(GuidString, ModuleList[GuidString.upper()].Name)
- MapBuffer.append(Line)
- #
- # Add the debug image full path.
- #
- MatchGuid = GuidName.match(Line)
- if MatchGuid is not None:
- GuidString = MatchGuid.group().split("=")[1]
- if GuidString.upper() in ModuleList:
- MapBuffer.append('(IMAGE=%s)\n' % (os.path.join(ModuleList[GuidString.upper()].DebugDir, ModuleList[GuidString.upper()].Name + '.efi')))
-
- 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
- for ModuleGuid in ModuleList:
- Module = ModuleList[ModuleGuid]
- 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, Module.DebugDir, ImageClass)
- if Module.ModuleType in [SUP_MODULE_PEI_CORE, SUP_MODULE_PEIM, EDK_COMPONENT_TYPE_COMBINED_PEIM_DRIVER, EDK_COMPONENT_TYPE_PIC_PEIM, EDK_COMPONENT_TYPE_RELOCATABLE_PEIM, SUP_MODULE_DXE_CORE]:
- PeiModuleList[Module.MetaFile] = ImageInfo
- PeiSize += ImageInfo.Image.Size
- elif Module.ModuleType in [EDK_COMPONENT_TYPE_BS_DRIVER, SUP_MODULE_DXE_DRIVER, SUP_MODULE_UEFI_DRIVER]:
- BtModuleList[Module.MetaFile] = ImageInfo
- BtSize += ImageInfo.Image.Size
- elif Module.ModuleType in [SUP_MODULE_DXE_RUNTIME_DRIVER, EDK_COMPONENT_TYPE_RT_DRIVER, SUP_MODULE_DXE_SAL_DRIVER, EDK_COMPONENT_TYPE_SAL_RT_DRIVER]:
- RtModuleList[Module.MetaFile] = ImageInfo
- RtSize += ImageInfo.Image.Size
- elif Module.ModuleType in [SUP_MODULE_SMM_CORE, SUP_MODULE_DXE_SMM_DRIVER, SUP_MODULE_MM_STANDALONE, SUP_MODULE_MM_CORE_STANDALONE]:
- SmmModuleList[Module.MetaFile] = ImageInfo
- SmmSize += ImageInfo.Image.Size
- if Module.ModuleType == SUP_MODULE_DXE_SMM_DRIVER:
- PiSpecVersion = Module.Module.Specification.get('PI_SPECIFICATION_VERSION', '0x00000000')
- # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver.
- if int(PiSpecVersion, 16) < 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_SET:
- 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_SET:
- 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")
-
- #
- # 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.append('PEI_CODE_PAGE_NUMBER = 0x%x\n' % (PeiSize // 0x1000))
- MapBuffer.append('BOOT_CODE_PAGE_NUMBER = 0x%x\n' % (BtSize // 0x1000))
- MapBuffer.append('RUNTIME_CODE_PAGE_NUMBER = 0x%x\n' % (RtSize // 0x1000))
- if len (SmmModuleList) > 0:
- MapBuffer.append('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.append('\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, ''.join(MapBuffer), False)
- if self.LoadFixAddress != 0:
- sys.stdout.write ("\nLoad Module At Fix Address Map file can be found at %s\n" % (MapFilePath))
- sys.stdout.flush()
-
- ## Build active platform for different build targets and different tool chains
- #
- def _BuildPlatform(self):
- SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False)
- for BuildTarget in self.BuildTargetList:
- GlobalData.gGlobalDefines['TARGET'] = BuildTarget
- index = 0
- for ToolChain in self.ToolChainList:
- GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain
- GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain
- GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]
- index += 1
- Wa = WorkspaceAutoGen(
- self.WorkspaceDir,
- self.PlatformFile,
- BuildTarget,
- ToolChain,
- self.ArchList,
- self.BuildDatabase,
- self.TargetTxt,
- self.ToolDef,
- self.Fdf,
- self.FdList,
- self.FvList,
- self.CapList,
- self.SkuId,
- self.UniFlag,
- self.Progress
- )
- self.Fdf = Wa.FdfFile
- self.LoadFixAddress = Wa.Platform.LoadFixAddress
- self.BuildReport.AddPlatformReport(Wa)
- self.Progress.Stop("done!")
-
- # Add ffs build to makefile
- CmdListDict = {}
- if GlobalData.gEnableGenfdsMultiThread and self.Fdf:
- CmdListDict = self._GenFfsCmd(Wa.ArchList)
-
- for Arch in Wa.ArchList:
- PcdMaList = []
- GlobalData.gGlobalDefines['ARCH'] = Arch
- Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)
- 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,Pa.DataPipe)
- if Ma is None:
- continue
- if Ma.PcdIsDriver:
- Ma.PlatformInfo = Pa
- Ma.Workspace = Wa
- PcdMaList.append(Ma)
- self.BuildModules.append(Ma)
- Pa.DataPipe.DataContainer = {"FfsCommand":CmdListDict}
- Pa.DataPipe.DataContainer = {"Workspace_timestamp": Wa._SrcTimeStamp}
- self._BuildPa(self.Target, Pa, FfsCommand=CmdListDict,PcdMaList=PcdMaList)
-
- # Create MAP file when Load Fix Address is enabled.
- if self.Target in ["", "all", "fds"]:
- for Arch in Wa.ArchList:
- GlobalData.gGlobalDefines['ARCH'] = Arch
- #
- # 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 platform with IA32 or ARM arch modules")
- #
- # Get Module List
- #
- ModuleList = {}
- for Pa in Wa.AutoGenObjectList:
- for Ma in Pa.ModuleAutoGenList:
- if Ma is None:
- continue
- if not Ma.IsLibrary:
- ModuleList[Ma.Guid.upper()] = Ma
-
- MapBuffer = []
- if self.LoadFixAddress != 0:
- #
- # 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, ModuleList)
- #
- # Save MAP buffer into MAP file.
- #
- self._SaveMapFile (MapBuffer, Wa)
- self.CreateGuidedSectionToolsFile(Wa)
-
- ## Build active module for different build targets, different tool chains and different archs
- #
- def _BuildModule(self):
- for BuildTarget in self.BuildTargetList:
- GlobalData.gGlobalDefines['TARGET'] = BuildTarget
- index = 0
- for ToolChain in self.ToolChainList:
- WorkspaceAutoGenTime = time.time()
- GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain
- GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain
- GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]
- index += 1
- #
- # module build needs platform build information, so get platform
- # AutoGen first
- #
- Wa = WorkspaceAutoGen(
- self.WorkspaceDir,
- self.PlatformFile,
- BuildTarget,
- ToolChain,
- self.ArchList,
- self.BuildDatabase,
- self.TargetTxt,
- self.ToolDef,
- self.Fdf,
- self.FdList,
- self.FvList,
- self.CapList,
- self.SkuId,
- self.UniFlag,
- self.Progress,
- self.ModuleFile
- )
- self.Fdf = Wa.FdfFile
- self.LoadFixAddress = Wa.Platform.LoadFixAddress
- Wa.CreateMakeFile(False)
- # Add ffs build to makefile
- CmdListDict = None
- if GlobalData.gEnableGenfdsMultiThread and self.Fdf:
- CmdListDict = self._GenFfsCmd(Wa.ArchList)
-
- GlobalData.file_lock = mp.Lock()
- GlobalData.FfsCmd = CmdListDict
-
- self.Progress.Stop("done!")
- MaList = []
- ExitFlag = threading.Event()
- ExitFlag.clear()
- self.AutoGenTime += int(round((time.time() - WorkspaceAutoGenTime)))
- for Arch in Wa.ArchList:
- AutoGenStart = time.time()
- GlobalData.gGlobalDefines['ARCH'] = Arch
- Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)
- for Module in Pa.Platform.Modules:
- if self.ModuleFile.Dir == Module.Dir and self.ModuleFile.Name == Module.Name:
- Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile,Pa.DataPipe)
- if Ma is None:
- continue
- if Ma.PcdIsDriver:
- Ma.PlatformInfo = Pa
- Ma.Workspace = Wa
- MaList.append(Ma)
-
- if GlobalData.gUseHashCache and not GlobalData.gBinCacheDest and self.Target in [None, "", "all"]:
- if Ma.CanSkipbyPreMakeCache():
- continue
- else:
- self.PreMakeCacheMiss.add(Ma)
-
- # 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':
- self.Progress.Start("Generating code")
- Ma.CreateCodeFile(True)
- self.Progress.Stop("done!")
- if self.Target == "genc":
- return True
- if not self.SkipAutoGen or self.Target == 'genmake':
- self.Progress.Start("Generating makefile")
- if CmdListDict and self.Fdf and (Module.Path, Arch) in CmdListDict:
- Ma.CreateMakeFile(True, CmdListDict[Module.Path, Arch])
- del CmdListDict[Module.Path, Arch]
- else:
- Ma.CreateMakeFile(True)
- self.Progress.Stop("done!")
- if self.Target == "genmake":
- return True
-
- if GlobalData.gBinCacheSource and self.Target in [None, "", "all"]:
- if Ma.CanSkipbyMakeCache():
- continue
- else:
- self.MakeCacheMiss.add(Ma)
-
- self.BuildModules.append(Ma)
- self.AutoGenTime += int(round((time.time() - AutoGenStart)))
- MakeStart = time.time()
- for Ma in self.BuildModules:
- if not Ma.IsBinaryModule:
- Bt = BuildTask.New(ModuleMakeUnit(Ma, Pa.BuildCommand,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)
- self.MakeTime += int(round((time.time() - MakeStart)))
-
- MakeContiue = time.time()
- ExitFlag.set()
- BuildTask.WaitForComplete()
- self.CreateAsBuiltInf()
- if GlobalData.gBinCacheDest:
- self.GenDestCache()
- elif GlobalData.gUseHashCache and not GlobalData.gBinCacheSource:
- # Only for --hash
- # Update PreMakeCacheChain files
- self.GenLocalPreMakeCache()
- self.BuildModules = []
- self.MakeTime += int(round((time.time() - MakeContiue)))
- if BuildTask.HasError():
- EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
-
- self.BuildReport.AddPlatformReport(Wa, MaList)
- 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(Wa.ArchList), self.PlatformFile),
- ExtraData=self.ModuleFile
- )
- # Create MAP file when Load Fix Address is enabled.
- if self.Target == "fds" and self.Fdf:
- for Arch in Wa.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 is None:
- continue
- if not Ma.IsLibrary:
- ModuleList[Ma.Guid.upper()] = Ma
-
- MapBuffer = []
- if self.LoadFixAddress != 0:
- #
- # Rebase module to the preferred memory address before GenFds
- #
- self._CollectModuleMapBuffer(MapBuffer, ModuleList)
- #
- # create FDS again for the updated EFI image
- #
- GenFdsStart = time.time()
- self._Build("fds", Wa)
- self.GenFdsTime += int(round((time.time() - GenFdsStart)))
- #
- # Create MAP file for all platform FVs after GenFds.
- #
- self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)
- #
- # Save MAP buffer into MAP file.
- #
- self._SaveMapFile (MapBuffer, Wa)
-
- def _GenFfsCmd(self,ArchList):
- # convert dictionary of Cmd:(Inf,Arch)
- # to a new dictionary of (Inf,Arch):Cmd,Cmd,Cmd...
- CmdSetDict = defaultdict(set)
- GenFfsDict = GenFds.GenFfsMakefile('', GlobalData.gFdfParser, self, ArchList, GlobalData)
- for Cmd in GenFfsDict:
- tmpInf, tmpArch = GenFfsDict[Cmd]
- CmdSetDict[tmpInf, tmpArch].add(Cmd)
- return CmdSetDict
- def VerifyAutoGenFiles(self):
- AutoGenIdFile = os.path.join(GlobalData.gConfDirectory,".AutoGenIdFile.txt")
- try:
- with open(AutoGenIdFile) as fd:
- lines = fd.readlines()
- except:
- return None
- for line in lines:
- if "Arch" in line:
- ArchList = line.strip().split("=")[1].split("|")
- if "BuildDir" in line:
- BuildDir = line.split("=")[1].strip()
- if "PlatformGuid" in line:
- PlatformGuid = line.split("=")[1].strip()
- GlobalVarList = []
- for arch in ArchList:
- global_var = os.path.join(BuildDir, "GlobalVar_%s_%s.bin" % (str(PlatformGuid),arch))
- if not os.path.exists(global_var):
- return None
- GlobalVarList.append(global_var)
- for global_var in GlobalVarList:
- data_pipe = MemoryDataPipe()
- data_pipe.load(global_var)
- target = data_pipe.Get("P_Info").get("Target")
- toolchain = data_pipe.Get("P_Info").get("ToolChain")
- archlist = data_pipe.Get("P_Info").get("ArchList")
- Arch = data_pipe.Get("P_Info").get("Arch")
- active_p = data_pipe.Get("P_Info").get("ActivePlatform")
- workspacedir = data_pipe.Get("P_Info").get("WorkspaceDir")
- PackagesPath = os.getenv("PACKAGES_PATH")
- mws.setWs(workspacedir, PackagesPath)
- LibraryBuildDirectoryList = data_pipe.Get("LibraryBuildDirectoryList")
- ModuleBuildDirectoryList = data_pipe.Get("ModuleBuildDirectoryList")
-
- for m_build_dir in LibraryBuildDirectoryList:
- if not os.path.exists(os.path.join(m_build_dir,self.MakeFileName)):
- return None
- for m_build_dir in ModuleBuildDirectoryList:
- if not os.path.exists(os.path.join(m_build_dir,self.MakeFileName)):
- return None
- Wa = WorkSpaceInfo(
- workspacedir,active_p,target,toolchain,archlist
- )
- Pa = PlatformInfo(Wa, active_p, target, toolchain, Arch,data_pipe)
- Wa.AutoGenObjectList.append(Pa)
- return Wa
- def SetupMakeSetting(self,Wa):
- BuildModules = []
- for Pa in Wa.AutoGenObjectList:
- for m in Pa._MbList:
- ma = ModuleAutoGen(Wa,m.MetaFile, Pa.BuildTarget, Wa.ToolChain, Pa.Arch, Pa.MetaFile,Pa.DataPipe)
- BuildModules.append(ma)
- fdf_file = Wa.FlashDefinition
- if fdf_file:
- Fdf = FdfParser(fdf_file.Path)
- Fdf.ParseFile()
- GlobalData.gFdfParser = Fdf
- if Fdf.CurrentFdName and Fdf.CurrentFdName in Fdf.Profile.FdDict:
- FdDict = Fdf.Profile.FdDict[Fdf.CurrentFdName]
- for FdRegion in FdDict.RegionList:
- if str(FdRegion.RegionType) == 'FILE' and self.Platform.VpdToolGuid in str(FdRegion.RegionDataList):
- if int(FdRegion.Offset) % 8 != 0:
- EdkLogger.error("build", FORMAT_INVALID, 'The VPD Base Address %s must be 8-byte aligned.' % (FdRegion.Offset))
- Wa.FdfProfile = Fdf.Profile
- self.Fdf = Fdf
- else:
- self.Fdf = None
- return BuildModules
-
- ## Build a platform in multi-thread mode
- #
- def PerformAutoGen(self,BuildTarget,ToolChain):
- WorkspaceAutoGenTime = time.time()
- Wa = WorkspaceAutoGen(
- self.WorkspaceDir,
- self.PlatformFile,
- BuildTarget,
- ToolChain,
- self.ArchList,
- self.BuildDatabase,
- self.TargetTxt,
- self.ToolDef,
- self.Fdf,
- self.FdList,
- self.FvList,
- self.CapList,
- self.SkuId,
- self.UniFlag,
- self.Progress
- )
- self.Fdf = Wa.FdfFile
- self.LoadFixAddress = Wa.Platform.LoadFixAddress
- self.BuildReport.AddPlatformReport(Wa)
- Wa.CreateMakeFile(False)
-
- # Add ffs build to makefile
- CmdListDict = {}
- if GlobalData.gEnableGenfdsMultiThread and self.Fdf:
- CmdListDict = self._GenFfsCmd(Wa.ArchList)
-
- self.AutoGenTime += int(round((time.time() - WorkspaceAutoGenTime)))
- BuildModules = []
- for Arch in Wa.ArchList:
- PcdMaList = []
- AutoGenStart = time.time()
- GlobalData.gGlobalDefines['ARCH'] = Arch
- Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)
- if Pa is None:
- continue
- ModuleList = []
- for Inf in Pa.Platform.Modules:
- ModuleList.append(Inf)
- # Add the INF only list in FDF
- if GlobalData.gFdfParser is not None:
- for InfName in GlobalData.gFdfParser.Profile.InfList:
- Inf = PathClass(NormPath(InfName), self.WorkspaceDir, Arch)
- if Inf in Pa.Platform.Modules:
- continue
- ModuleList.append(Inf)
- Pa.DataPipe.DataContainer = {"FfsCommand":CmdListDict}
- Pa.DataPipe.DataContainer = {"Workspace_timestamp": Wa._SrcTimeStamp}
- Pa.DataPipe.DataContainer = {"CommandTarget": self.Target}
- Pa.CreateLibModuelDirs()
- # Fetch the MakeFileName.
- self.MakeFileName = Pa.MakeFileName
-
- Pa.DataPipe.DataContainer = {"LibraryBuildDirectoryList":Pa.LibraryBuildDirectoryList}
- Pa.DataPipe.DataContainer = {"ModuleBuildDirectoryList":Pa.ModuleBuildDirectoryList}
- Pa.DataPipe.DataContainer = {"FdsCommandDict": Wa.GenFdsCommandDict}
- # Prepare the cache share data for multiprocessing
- Pa.DataPipe.DataContainer = {"gPlatformHashFile":GlobalData.gPlatformHashFile}
- ModuleCodaFile = {}
- for ma in Pa.ModuleAutoGenList:
- ModuleCodaFile[(ma.MetaFile.File,ma.MetaFile.Root,ma.Arch,ma.MetaFile.Path)] = [item.Target for item in ma.CodaTargetList]
- Pa.DataPipe.DataContainer = {"ModuleCodaFile":ModuleCodaFile}
- # ModuleList contains all driver modules only
- for Module in ModuleList:
- # Get ModuleAutoGen object to generate C code file and makefile
- Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile,Pa.DataPipe)
- if Ma is None:
- continue
- if Ma.PcdIsDriver:
- Ma.PlatformInfo = Pa
- Ma.Workspace = Wa
- PcdMaList.append(Ma)
- self.AllDrivers.add(Ma)
- self.AllModules.add(Ma)
-
- mqueue = mp.Queue()
- cqueue = mp.Queue()
- for m in Pa.GetAllModuleInfo:
- mqueue.put(m)
- module_file,module_root,module_path,module_basename,\
- module_originalpath,module_arch,IsLib = m
- Ma = ModuleAutoGen(Wa, PathClass(module_path, Wa), BuildTarget,\
- ToolChain, Arch, self.PlatformFile,Pa.DataPipe)
- self.AllModules.add(Ma)
- data_pipe_file = os.path.join(Pa.BuildDir, "GlobalVar_%s_%s.bin" % (str(Pa.Guid),Pa.Arch))
- Pa.DataPipe.dump(data_pipe_file)
-
- mqueue.put((None,None,None,None,None,None,None))
- autogen_rt, errorcode = self.StartAutoGen(mqueue, Pa.DataPipe, self.SkipAutoGen, PcdMaList, cqueue)
-
- if not autogen_rt:
- self.AutoGenMgr.TerminateWorkers()
- self.AutoGenMgr.join(1)
- raise FatalError(errorcode)
-
- if GlobalData.gUseHashCache:
- for item in GlobalData.gModuleAllCacheStatus:
- (MetaFilePath, Arch, CacheStr, Status) = item
- Ma = ModuleAutoGen(Wa, PathClass(MetaFilePath, Wa), BuildTarget,\
- ToolChain, Arch, self.PlatformFile,Pa.DataPipe)
- if CacheStr == "PreMakeCache" and Status == False:
- self.PreMakeCacheMiss.add(Ma)
- if CacheStr == "PreMakeCache" and Status == True:
- self.PreMakeCacheHit.add(Ma)
- GlobalData.gModuleCacheHit.add(Ma)
- if CacheStr == "MakeCache" and Status == False:
- self.MakeCacheMiss.add(Ma)
- if CacheStr == "MakeCache" and Status == True:
- self.MakeCacheHit.add(Ma)
- GlobalData.gModuleCacheHit.add(Ma)
- self.AutoGenTime += int(round((time.time() - AutoGenStart)))
- AutoGenIdFile = os.path.join(GlobalData.gConfDirectory,".AutoGenIdFile.txt")
- with open(AutoGenIdFile,"w") as fw:
- fw.write("Arch=%s\n" % "|".join((Wa.ArchList)))
- fw.write("BuildDir=%s\n" % Wa.BuildDir)
- fw.write("PlatformGuid=%s\n" % str(Wa.AutoGenObjectList[0].Guid))
-
- if GlobalData.gBinCacheSource:
- BuildModules.extend(self.MakeCacheMiss)
- elif GlobalData.gUseHashCache and not GlobalData.gBinCacheDest:
- BuildModules.extend(self.PreMakeCacheMiss)
- else:
- BuildModules.extend(self.AllDrivers)
-
- self.Progress.Stop("done!")
- return Wa, BuildModules
-
- def _MultiThreadBuildPlatform(self):
- SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False)
- for BuildTarget in self.BuildTargetList:
- GlobalData.gGlobalDefines['TARGET'] = BuildTarget
- index = 0
- for ToolChain in self.ToolChainList:
- resetFdsGlobalVariable()
- GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain
- GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain
- GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]
- index += 1
- ExitFlag = threading.Event()
- ExitFlag.clear()
- if self.SkipAutoGen:
- Wa = self.VerifyAutoGenFiles()
- if Wa is None:
- self.SkipAutoGen = False
- Wa, self.BuildModules = self.PerformAutoGen(BuildTarget,ToolChain)
- else:
- GlobalData.gAutoGenPhase = True
- self.BuildModules = self.SetupMakeSetting(Wa)
- else:
- Wa, self.BuildModules = self.PerformAutoGen(BuildTarget,ToolChain)
- Pa = Wa.AutoGenObjectList[0]
- GlobalData.gAutoGenPhase = False
-
- if GlobalData.gBinCacheSource:
- EdkLogger.quiet("[cache Summary]: Total module num: %s" % len(self.AllModules))
- EdkLogger.quiet("[cache Summary]: PreMakecache miss num: %s " % len(self.PreMakeCacheMiss))
- EdkLogger.quiet("[cache Summary]: Makecache miss num: %s " % len(self.MakeCacheMiss))
-
- for Arch in Wa.ArchList:
- MakeStart = time.time()
- for Ma in set(self.BuildModules):
- # Generate build task for the module
- if not Ma.IsBinaryModule:
- Bt = BuildTask.New(ModuleMakeUnit(Ma, Pa.BuildCommand,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
-
- if BuildTask.HasError():
- EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
- self.MakeTime += int(round((time.time() - MakeStart)))
-
- MakeContiue = time.time()
- #
- #
- # All modules have been put in build tasks queue. Tell task scheduler
- # to exit if all tasks are completed
- #
- ExitFlag.set()
- BuildTask.WaitForComplete()
- if GlobalData.gBinCacheDest:
- self.GenDestCache()
- elif GlobalData.gUseHashCache and not GlobalData.gBinCacheSource:
- # Only for --hash
- # Update PreMakeCacheChain files
- self.GenLocalPreMakeCache()
- #
- # Get Module List
- #
- ModuleList = {ma.Guid.upper(): ma for ma in self.BuildModules}
- self.BuildModules = []
- self.MakeTime += int(round((time.time() - MakeContiue)))
- #
- # 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"]:
- for Arch in Wa.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")
-
- #
- # Rebase module to the preferred memory address before GenFds
- #
- MapBuffer = []
- if self.LoadFixAddress != 0:
- self._CollectModuleMapBuffer(MapBuffer, ModuleList)
-
- if self.Fdf:
- #
- # Generate FD image if there's a FDF file found
- #
- GenFdsStart = time.time()
- if GenFdsApi(Wa.GenFdsCommandDict, self.Db):
- EdkLogger.error("build", COMMAND_FAILURE)
- Threshold = self.GetFreeSizeThreshold()
- if Threshold:
- self.CheckFreeSizeThreshold(Threshold, Wa.FvDir)
-
- #
- # Create MAP file for all platform FVs after GenFds.
- #
- self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)
- self.GenFdsTime += int(round((time.time() - GenFdsStart)))
- #
- # Save MAP buffer into MAP file.
- #
- self._SaveMapFile(MapBuffer, Wa)
- self.CreateGuidedSectionToolsFile(Wa)
-
- ## GetFreeSizeThreshold()
- #
- # @retval int Threshold value
- #
- def GetFreeSizeThreshold(self):
- Threshold = None
- Threshold_Str = GlobalData.gCommandLineDefines.get('FV_SPARE_SPACE_THRESHOLD')
- if Threshold_Str:
- try:
- if Threshold_Str.lower().startswith('0x'):
- Threshold = int(Threshold_Str, 16)
- else:
- Threshold = int(Threshold_Str)
- except:
- EdkLogger.warn("build", 'incorrect value for FV_SPARE_SPACE_THRESHOLD %s.Only decimal or hex format is allowed.' % Threshold_Str)
- return Threshold
-
- def CheckFreeSizeThreshold(self, Threshold=None, FvDir=None):
- if not isinstance(Threshold, int):
- return
- if not isinstance(FvDir, str) or not FvDir:
- return
- FdfParserObject = GlobalData.gFdfParser
- FvRegionNameList = [FvName for FvName in FdfParserObject.Profile.FvDict if FdfParserObject.Profile.FvDict[FvName].FvRegionInFD]
- for FvName in FdfParserObject.Profile.FvDict:
- if FvName in FvRegionNameList:
- FvSpaceInfoFileName = os.path.join(FvDir, FvName.upper() + '.Fv.map')
- if os.path.exists(FvSpaceInfoFileName):
- FileLinesList = getlines(FvSpaceInfoFileName)
- for Line in FileLinesList:
- NameValue = Line.split('=')
- if len(NameValue) == 2 and NameValue[0].strip() == 'EFI_FV_SPACE_SIZE':
- FreeSizeValue = int(NameValue[1].strip(), 0)
- if FreeSizeValue < Threshold:
- EdkLogger.error("build", FV_FREESIZE_ERROR,
- '%s FV free space %d is not enough to meet with the required spare space %d set by -D FV_SPARE_SPACE_THRESHOLD option.' % (
- FvName, FreeSizeValue, Threshold))
- break
-
- ## Generate GuidedSectionTools.txt in the FV directories.
- #
- def CreateGuidedSectionToolsFile(self,Wa):
- for BuildTarget in self.BuildTargetList:
- for ToolChain in self.ToolChainList:
- FvDir = Wa.FvDir
- if not os.path.exists(FvDir):
- continue
- for Arch in self.ArchList:
- guidList = []
- tooldefguidList = []
- guidAttribs = []
- for Platform in Wa.AutoGenObjectList:
- if Platform.BuildTarget != BuildTarget:
- continue
- if Platform.ToolChain != ToolChain:
- continue
- if Platform.Arch != Arch:
- continue
- if hasattr (Platform, 'BuildOption'):
- for Tool in Platform.BuildOption:
- if 'GUID' in Platform.BuildOption[Tool]:
- if 'PATH' in Platform.BuildOption[Tool]:
- value = Platform.BuildOption[Tool]['GUID']
- if value in guidList:
- EdkLogger.error("build", FORMAT_INVALID, "Duplicate GUID value %s used with Tool %s in DSC [BuildOptions]." % (value, Tool))
- path = Platform.BuildOption[Tool]['PATH']
- guidList.append(value)
- guidAttribs.append((value, Tool, path))
- for Tool in Platform.ToolDefinition:
- if 'GUID' in Platform.ToolDefinition[Tool]:
- if 'PATH' in Platform.ToolDefinition[Tool]:
- value = Platform.ToolDefinition[Tool]['GUID']
- if value in tooldefguidList:
- EdkLogger.error("build", FORMAT_INVALID, "Duplicate GUID value %s used with Tool %s in tools_def.txt." % (value, Tool))
- tooldefguidList.append(value)
- if value in guidList:
- # Already added by platform
- continue
- path = Platform.ToolDefinition[Tool]['PATH']
- guidList.append(value)
- guidAttribs.append((value, Tool, path))
- # Sort by GuidTool name
- guidAttribs = sorted (guidAttribs, key=lambda x: x[1])
- # Write out GuidedSecTools.txt
- toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')
- toolsFile = open(toolsFile, 'wt')
- for guidedSectionTool in guidAttribs:
- print(' '.join(guidedSectionTool), file=toolsFile)
- toolsFile.close()
-
- ## Returns the real path of the tool.
- #
- def GetRealPathOfTool (self, tool):
- if os.path.exists(tool):
- return os.path.realpath(tool)
- return tool
-
- ## Launch the module or platform build
- #
- def Launch(self):
- self.AllDrivers = set()
- self.AllModules = set()
- self.PreMakeCacheMiss = set()
- self.PreMakeCacheHit = set()
- self.MakeCacheMiss = set()
- self.MakeCacheHit = set()
- if not self.ModuleFile:
- if not self.SpawnMode or self.Target not in ["", "all"]:
- self.SpawnMode = False
- self._BuildPlatform()
- else:
- self._MultiThreadBuildPlatform()
- else:
- self.SpawnMode = False
- self._BuildModule()
-
- if self.Target == 'cleanall':
- RemoveDirectory(os.path.dirname(GlobalData.gDatabasePath), True)
-
- def CreateAsBuiltInf(self):
- for Module in self.BuildModules:
- Module.CreateAsBuiltInf()
-
- def GenDestCache(self):
- for Module in self.AllModules:
- Module.GenPreMakefileHashList()
- Module.GenMakefileHashList()
- Module.CopyModuleToCache()
-
- def GenLocalPreMakeCache(self):
- for Module in self.PreMakeCacheMiss:
- Module.GenPreMakefileHashList()
-
- ## Do some clean-up works when error occurred
- def Relinquish(self):
- OldLogLevel = EdkLogger.GetLevel()
- EdkLogger.SetLevel(EdkLogger.ERROR)
- Utils.Progressor.Abort()
- if self.SpawnMode == True:
- BuildTask.Abort()
- EdkLogger.SetLevel(OldLogLevel)
-
-def ParseDefines(DefineList=[]):
- DefineDict = {}
- if DefineList is not None:
- for Define in DefineList:
- DefineTokenList = Define.split("=", 1)
- if not GlobalData.gMacroNamePattern.match(DefineTokenList[0]):
- EdkLogger.error('build', FORMAT_INVALID,
- "The macro name must be in the pattern [A-Z][A-Z0-9_]*",
- ExtraData=DefineTokenList[0])
-
- if len(DefineTokenList) == 1:
- DefineDict[DefineTokenList[0]] = "TRUE"
- else:
- DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()
- return DefineDict
-
-
-
-def LogBuildTime(Time):
- if Time:
- TimeDurStr = ''
- TimeDur = time.gmtime(Time)
- if TimeDur.tm_yday > 1:
- TimeDurStr = time.strftime("%H:%M:%S", TimeDur) + ", %d day(s)" % (TimeDur.tm_yday - 1)
- else:
- TimeDurStr = time.strftime("%H:%M:%S", TimeDur)
- return TimeDurStr
- else:
- return None
-def ThreadNum():
- OptionParser = MyOptionParser()
- if not OptionParser.BuildOption and not OptionParser.BuildTarget:
- OptionParser.GetOption()
- BuildOption, BuildTarget = OptionParser.BuildOption, OptionParser.BuildTarget
- ThreadNumber = BuildOption.ThreadNumber
- GlobalData.gCmdConfDir = BuildOption.ConfDirectory
- if ThreadNumber is None:
- TargetObj = TargetTxtDict()
- ThreadNumber = TargetObj.Target.TargetTxtDictionary[TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]
- if ThreadNumber == '':
- ThreadNumber = 0
- else:
- ThreadNumber = int(ThreadNumber, 0)
-
- if ThreadNumber == 0:
- try:
- ThreadNumber = multiprocessing.cpu_count()
- except (ImportError, NotImplementedError):
- ThreadNumber = 1
- return ThreadNumber
-## 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
-#
-LogQMaxSize = ThreadNum() * 10
-def Main():
- StartTime = time.time()
-
- #
- # Create a log Queue
- #
- LogQ = mp.Queue(LogQMaxSize)
- # Initialize log system
- EdkLogger.LogClientInitialize(LogQ)
- GlobalData.gCommand = sys.argv[1:]
- #
- # Parse the options and args
- #
- OptionParser = MyOptionParser()
- if not OptionParser.BuildOption and not OptionParser.BuildTarget:
- OptionParser.GetOption()
- Option, Target = OptionParser.BuildOption, OptionParser.BuildTarget
- GlobalData.gOptions = Option
- GlobalData.gCaseInsensitive = Option.CaseInsensitive
-
- # Set log level
- LogLevel = EdkLogger.INFO
- if Option.verbose is not None:
- EdkLogger.SetLevel(EdkLogger.VERBOSE)
- LogLevel = EdkLogger.VERBOSE
- elif Option.quiet is not None:
- EdkLogger.SetLevel(EdkLogger.QUIET)
- LogLevel = EdkLogger.QUIET
- elif Option.debug is not None:
- EdkLogger.SetLevel(Option.debug + 1)
- LogLevel = Option.debug + 1
- else:
- EdkLogger.SetLevel(EdkLogger.INFO)
-
- if Option.WarningAsError == True:
- EdkLogger.SetWarningAsError()
- Log_Agent = LogAgent(LogQ,LogLevel,Option.LogFile)
- Log_Agent.start()
-
- if platform.platform().find("Windows") >= 0:
- GlobalData.gIsWindows = True
- else:
- GlobalData.gIsWindows = False
-
- EdkLogger.quiet("Build environment: %s" % platform.platform())
- EdkLogger.quiet(time.strftime("Build start time: %H:%M:%S, %b.%d %Y\n", time.localtime()));
- ReturnCode = 0
- MyBuild = None
- BuildError = True
- 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)))
-
- #
- # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH
- #
- CheckEnvVariable()
- GlobalData.gCommandLineDefines.update(ParseDefines(Option.Macros))
-
- 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 is not 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)
-
- if Option.FdfFile is not 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)
-
- if Option.Flag is not None and Option.Flag not in ['-c', '-s']:
- EdkLogger.error("build", OPTION_VALUE_INVALID, "UNI flag must be one of -c or -s")
-
- MyBuild = Build(Target, Workspace, Option,LogQ)
- GlobalData.gCommandLineDefines['ARCH'] = ' '.join(MyBuild.ArchList)
- if not (MyBuild.LaunchPrebuildFlag and os.path.exists(MyBuild.PlatformBuildPath)):
- MyBuild.Launch()
-
- #
- # All job done, no error found and no exception raised
- #
- BuildError = False
- except FatalError as X:
- if MyBuild is not None:
- # for multi-thread build exits safely
- MyBuild.Relinquish()
- if Option is not None and Option.debug is not None:
- EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
- ReturnCode = X.args[0]
- except Warning as X:
- # error from Fdf parser
- if MyBuild is not None:
- # for multi-thread build exits safely
- MyBuild.Relinquish()
- if Option is not None and Option.debug is not 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:
- if MyBuild is not None:
-
- # for multi-thread build exits safely
- MyBuild.Relinquish()
- ReturnCode = ABORT_ERROR
- if Option is not None and Option.debug is not None:
- EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
- except:
- if MyBuild is not 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 is not 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 %s for help, attaching following call stack trace!)\n" % MSG_EDKII_MAIL_ADDR,
- RaiseError=False
- )
- EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
- ReturnCode = CODE_ERROR
- finally:
- Utils.Progressor.Abort()
- Utils.ClearDuplicatedInf()
-
- if ReturnCode == 0:
- try:
- MyBuild.LaunchPostbuild()
- Conclusion = "Done"
- except:
- Conclusion = "Failed"
- ReturnCode = POSTBUILD_ERROR
- elif ReturnCode == ABORT_ERROR:
- Conclusion = "Aborted"
- else:
- Conclusion = "Failed"
- FinishTime = time.time()
- BuildDuration = time.gmtime(int(round(FinishTime - StartTime)))
- BuildDurationStr = ""
- if BuildDuration.tm_yday > 1:
- BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration) + ", %d day(s)" % (BuildDuration.tm_yday - 1)
- else:
- BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration)
- if MyBuild is not None:
- if not BuildError:
- MyBuild.BuildReport.GenerateReport(BuildDurationStr, LogBuildTime(MyBuild.AutoGenTime), LogBuildTime(MyBuild.MakeTime), LogBuildTime(MyBuild.GenFdsTime))
-
- EdkLogger.SetLevel(EdkLogger.QUIET)
- EdkLogger.quiet("\n- %s -" % Conclusion)
- EdkLogger.quiet(time.strftime("Build end time: %H:%M:%S, %b.%d %Y", time.localtime()))
- EdkLogger.quiet("Build total time: %s\n" % BuildDurationStr)
- Log_Agent.kill()
- Log_Agent.join()
- return ReturnCode
-
-if __name__ == '__main__':
- try:
- mp.set_start_method('spawn')
- except:
- pass
- 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) 2014, Hewlett-Packard Development Company, L.P.<BR> +# Copyright (c) 2007 - 2021, Intel Corporation. All rights reserved.<BR> +# Copyright (c) 2018, Hewlett Packard Enterprise Development, L.P.<BR> +# Copyright (c) 2020 - 2021, ARM Limited. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +## +# Import Modules +# +from __future__ import print_function +from __future__ import absolute_import +import os.path as path +import sys +import os +import re +import glob +import time +import platform +import traceback +import multiprocessing +from threading import Thread,Event,BoundedSemaphore +import threading +from linecache import getlines +from subprocess import Popen,PIPE, STDOUT +from collections import OrderedDict, defaultdict + +from AutoGen.PlatformAutoGen import PlatformAutoGen +from AutoGen.ModuleAutoGen import ModuleAutoGen +from AutoGen.WorkspaceAutoGen import WorkspaceAutoGen +from AutoGen.AutoGenWorker import AutoGenWorkerInProcess,AutoGenManager,\ + LogAgent +from AutoGen import GenMake +from Common import Misc as Utils + +from Common.TargetTxtClassObject import TargetTxtDict +from Common.ToolDefClassObject import ToolDefDict +from buildoptions import MyOptionParser +from Common.Misc import PathClass,SaveFileOnChange,RemoveDirectory +from Common.StringUtils import NormPath +from Common.MultipleWorkspace import MultipleWorkspace as mws +from Common.BuildToolError import * +from Common.DataType import * +import Common.EdkLogger as EdkLogger + +from Workspace.WorkspaceDatabase import BuildDB + +from BuildReport import BuildReport +from GenPatchPcdTable.GenPatchPcdTable import PeImageClass,parsePcdInfoFromMapFile +from PatchPcdValue.PatchPcdValue import PatchBinaryFile + +import Common.GlobalData as GlobalData +from GenFds.GenFds import GenFds, GenFdsApi +import multiprocessing as mp +from multiprocessing import Manager +from AutoGen.DataPipe import MemoryDataPipe +from AutoGen.ModuleAutoGenHelper import WorkSpaceInfo, PlatformInfo +from GenFds.FdfParser import FdfParser +from AutoGen.IncludesAutoGen import IncludesAutoGen +from GenFds.GenFds import resetFdsGlobalVariable +from AutoGen.AutoGen import CalculatePriorityValue + +## standard targets of build command +gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run'] + +TemporaryTablePattern = re.compile(r'^_\d+_\d+_[a-fA-F0-9]+$') +TmpTableDict = {} + +## 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 'PATHEXT' in os.environ: + 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=WorkspaceDir) + elif ' ' in WorkspaceDir: + EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in WORKSPACE path", + ExtraData=WorkspaceDir) + os.environ["WORKSPACE"] = WorkspaceDir + + # set multiple workspace + PackagesPath = os.getenv("PACKAGES_PATH") + mws.setWs(WorkspaceDir, PackagesPath) + if mws.PACKAGES_PATH: + for Path in mws.PACKAGES_PATH: + if not os.path.exists(Path): + EdkLogger.error("build", FILE_NOT_FOUND, "One Path in PACKAGES_PATH doesn't exist", ExtraData=Path) + elif ' ' in Path: + EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in PACKAGES_PATH", ExtraData=Path) + + + os.environ["EDK_TOOLS_PATH"] = os.path.normcase(os.environ["EDK_TOOLS_PATH"]) + + # 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.gGlobalDefines["WORKSPACE"] = WorkspaceDir + GlobalData.gGlobalDefines["EDK_TOOLS_PATH"] = os.environ["EDK_TOOLS_PATH"] + +## 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(mws.join(Workspace, FilePath)) + Workspace = mws.getWs(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,MemTo=None): + while True: + # read one line a time + Line = From.readline() + # empty string means "end" + if Line is not None and Line != b"": + LineStr = Line.rstrip().decode(encoding='utf-8', errors='ignore') + if MemTo is not None: + if "Note: including file:" == LineStr.lstrip()[:21]: + MemTo.append(LineStr) + else: + To(LineStr) + MemTo.append(LineStr) + else: + To(LineStr) + else: + break + if ExitFlag.is_set(): + break + +class MakeSubProc(Popen): + def __init__(self,*args, **argv): + super(MakeSubProc,self).__init__(*args, **argv) + self.ProcOut = [] + +## 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 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,ModuleAuto = None): + BeginTime = time.time() + # 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) + + # Command is used as the first Argument in following Popen(). + # It could be a string or sequence. We find that if command is a string in following Popen(), + # ubuntu may fail with an error message that the command is not found. + # So here we may need convert command from string to list instance. + if platform.system() != 'Windows': + if not isinstance(Command, list): + Command = Command.split() + Command = ' '.join(Command) + + Proc = None + EndOfProcedure = None + try: + # launch the command + Proc = MakeSubProc(Command, stdout=PIPE, stderr=STDOUT, env=os.environ, cwd=WorkingDir, bufsize=-1, shell=True) + + # 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,Proc.ProcOut)) + StdOutThread.name = "STDOUT-Redirector" + StdOutThread.daemon = False + StdOutThread.start() + + + # waiting for program exit + Proc.wait() + except: # in case of aborting + # terminate the threads redirecting the program output + EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc()) + if EndOfProcedure is not None: + EndOfProcedure.set() + if Proc is None: + if not isinstance(Command, type("")): + Command = " ".join(Command) + EdkLogger.error("build", COMMAND_FAILURE, "Failed to start command", ExtraData="%s [%s]" % (Command, WorkingDir)) + + if Proc.stdout: + StdOutThread.join() + + # check the return code of the program + if Proc.returncode != 0: + if not isinstance(Command, type("")): + Command = " ".join(Command) + # print out the Response file and its content when make failure + RespFile = os.path.join(WorkingDir, 'OUTPUT', 'respfilelist.txt') + if os.path.isfile(RespFile): + f = open(RespFile) + RespContent = f.read() + f.close() + EdkLogger.info(RespContent) + + EdkLogger.error("build", COMMAND_FAILURE, ExtraData="%s [%s]" % (Command, WorkingDir)) + if ModuleAuto: + iau = IncludesAutoGen(WorkingDir,ModuleAuto) + if ModuleAuto.ToolChainFamily == TAB_COMPILER_MSFT: + iau.CreateDepsFileForMsvc(Proc.ProcOut) + else: + iau.UpdateDepsFileforNonMsvc() + iau.UpdateDepsFileforTrim() + iau.CreateModuleDeps() + iau.CreateDepsInclude() + iau.CreateDepsTarget() + return "%dms" % (int(round((time.time() - BeginTime) * 1000))) + +## 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 not BuildCommand: + EdkLogger.error("build", OPTION_MISSING, + "No build command found for this module. " + "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." % + (Obj.BuildTarget, Obj.ToolChain, Obj.Arch), + ExtraData=str(Obj)) + + + ## str() method + # + # It just returns the string representation 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 and self.BuildObject == Other.BuildObject \ + and 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, BuildCommand,Target): + Dependency = [ModuleMakeUnit(La, BuildCommand,Target) for La in Obj.LibraryAutoGenList] + BuildUnit.__init__(self, 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, BuildCommand, Target): + Dependency = [ModuleMakeUnit(Lib, BuildCommand, Target) for Lib in self.BuildObject.LibraryAutoGenList] + Dependency.extend([ModuleMakeUnit(Mod, BuildCommand,Target) for Mod in self.BuildObject.ModuleAutoGenList]) + BuildUnit.__init__(self, 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 = OrderedDict() + _PendingQueueLock = threading.Lock() + + # queue for tasks ready for running + _ReadyQueue = OrderedDict() + _ReadyQueueLock = threading.Lock() + + # queue for run tasks + _RunningQueue = OrderedDict() + _RunningQueueLock = threading.Lock() + + # queue containing all build tasks, in case duplicate build + _TaskQueue = OrderedDict() + + # 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.name = "Build-Task-Scheduler" + SchedulerThread.daemon = 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.is_set()) and not BuildTask._ErrorFlag.is_set(): + 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 = list(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.is_set(): + # 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, Bt = BuildTask._ReadyQueue.popitem() + + # 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.is_set(): + EdkLogger.quiet("\nWaiting for all build threads exit...") + # while not BuildTask._ErrorFlag.is_set() 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.name for Th in threading.enumerate())) + # avoid tense loop + time.sleep(0.1) + except BaseException as X: + # + # TRICK: hide the output of threads left running, 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.is_set() + + ## 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.is_set() + + ## 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 is 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: + if not Dep.BuildObject.IsBinaryModule and not Dep.BuildObject.CanSkipbyCache(GlobalData.gModuleCacheHit): + 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: + self.BuildItem.BuildObject.BuildTime = LaunchCommand(Command, WorkingDir,self.BuildItem.BuildObject) + self.CompleteFlag = True + + # Run hash operation post dependency to account for libs + # Run if --hash or --binary-destination + if GlobalData.gUseHashCache and not GlobalData.gBinCacheSource: + self.BuildItem.BuildObject.GenModuleHash() + if GlobalData.gBinCacheDest: + self.BuildItem.BuildObject.GenCMakeHash() + + except: + # + # TRICK: hide the output of threads left running, so that the user can + # catch the error message easily + # + if not BuildTask._ErrorFlag.is_set(): + 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.current_thread().name, 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.name = "build thread" + self.BuildTread.daemon = 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 OutputDir The output directory for image. + # @param DebugDir The debug directory for image. + # @param ImageClass PeImage Information + # + def __init__(self, BaseName, Guid, Arch, OutputDir, DebugDir, ImageClass): + self.BaseName = BaseName + self.Guid = Guid + self.Arch = Arch + self.OutputDir = OutputDir + self.DebugDir = DebugDir + 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 BuildOptions Build options passed from command line + # + def __init__(self, Target, WorkspaceDir, BuildOptions,log_q): + self.WorkspaceDir = WorkspaceDir + self.Target = Target + self.PlatformFile = BuildOptions.PlatformFile + self.ModuleFile = BuildOptions.ModuleFile + self.ArchList = BuildOptions.TargetArch + self.ToolChainList = BuildOptions.ToolChain + self.BuildTargetList= BuildOptions.BuildTarget + self.Fdf = BuildOptions.FdfFile + self.FdList = BuildOptions.RomImage + self.FvList = BuildOptions.FvImage + self.CapList = BuildOptions.CapName + self.SilentMode = BuildOptions.SilentMode + self.ThreadNumber = 1 + self.SkipAutoGen = BuildOptions.SkipAutoGen + self.Reparse = BuildOptions.Reparse + self.SkuId = BuildOptions.SkuId + if self.SkuId: + GlobalData.gSKUID_CMD = self.SkuId + self.ConfDirectory = BuildOptions.ConfDirectory + self.SpawnMode = True + self.BuildReport = BuildReport(BuildOptions.ReportFile, BuildOptions.ReportType) + self.AutoGenTime = 0 + self.MakeTime = 0 + self.GenFdsTime = 0 + self.MakeFileName = "" + TargetObj = TargetTxtDict() + ToolDefObj = ToolDefDict((os.path.join(os.getenv("WORKSPACE"),"Conf"))) + self.TargetTxt = TargetObj.Target + self.ToolDef = ToolDefObj.ToolDef + GlobalData.BuildOptionPcd = BuildOptions.OptionPcd if BuildOptions.OptionPcd else [] + #Set global flag for build mode + GlobalData.gIgnoreSource = BuildOptions.IgnoreSources + GlobalData.gUseHashCache = BuildOptions.UseHashCache + GlobalData.gBinCacheDest = BuildOptions.BinCacheDest + GlobalData.gBinCacheSource = BuildOptions.BinCacheSource + GlobalData.gEnableGenfdsMultiThread = not BuildOptions.NoGenfdsMultiThread + GlobalData.gDisableIncludePathCheck = BuildOptions.DisableIncludePathCheck + + if GlobalData.gBinCacheDest and not GlobalData.gUseHashCache: + EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-destination must be used together with --hash.") + + if GlobalData.gBinCacheSource and not GlobalData.gUseHashCache: + EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-source must be used together with --hash.") + + if GlobalData.gBinCacheDest and GlobalData.gBinCacheSource: + EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-destination can not be used together with --binary-source.") + + if GlobalData.gBinCacheSource: + BinCacheSource = os.path.normpath(GlobalData.gBinCacheSource) + if not os.path.isabs(BinCacheSource): + BinCacheSource = mws.join(self.WorkspaceDir, BinCacheSource) + GlobalData.gBinCacheSource = BinCacheSource + else: + if GlobalData.gBinCacheSource is not None: + EdkLogger.error("build", OPTION_VALUE_INVALID, ExtraData="Invalid value of option --binary-source.") + + if GlobalData.gBinCacheDest: + BinCacheDest = os.path.normpath(GlobalData.gBinCacheDest) + if not os.path.isabs(BinCacheDest): + BinCacheDest = mws.join(self.WorkspaceDir, BinCacheDest) + GlobalData.gBinCacheDest = BinCacheDest + else: + if GlobalData.gBinCacheDest is not None: + EdkLogger.error("build", OPTION_VALUE_INVALID, ExtraData="Invalid value of option --binary-destination.") + + GlobalData.gDatabasePath = os.path.normpath(os.path.join(GlobalData.gConfDirectory, GlobalData.gDatabasePath)) + if not os.path.exists(os.path.join(GlobalData.gConfDirectory, '.cache')): + os.makedirs(os.path.join(GlobalData.gConfDirectory, '.cache')) + self.Db = BuildDB + self.BuildDatabase = self.Db.BuildObject + self.Platform = None + self.ToolChainFamily = None + self.LoadFixAddress = 0 + self.UniFlag = BuildOptions.Flag + self.BuildModules = [] + self.HashSkipModules = [] + self.Db_Flag = False + self.LaunchPrebuildFlag = False + self.PlatformBuildPath = os.path.join(GlobalData.gConfDirectory, '.cache', '.PlatformBuild') + if BuildOptions.CommandLength: + GlobalData.gCommandMaxLength = BuildOptions.CommandLength + + # print dot character during doing some time-consuming work + self.Progress = Utils.Progressor() + # print current build environment and configuration + EdkLogger.quiet("%-16s = %s" % ("WORKSPACE", os.environ["WORKSPACE"])) + if "PACKAGES_PATH" in os.environ: + # WORKSPACE env has been converted before. Print the same path style with WORKSPACE env. + EdkLogger.quiet("%-16s = %s" % ("PACKAGES_PATH", os.path.normcase(os.path.normpath(os.environ["PACKAGES_PATH"])))) + EdkLogger.quiet("%-16s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"])) + if "EDK_TOOLS_BIN" in os.environ: + # Print the same path style with WORKSPACE env. + EdkLogger.quiet("%-16s = %s" % ("EDK_TOOLS_BIN", os.path.normcase(os.path.normpath(os.environ["EDK_TOOLS_BIN"])))) + EdkLogger.quiet("%-16s = %s" % ("CONF_PATH", GlobalData.gConfDirectory)) + if "PYTHON3_ENABLE" in os.environ: + PYTHON3_ENABLE = os.environ["PYTHON3_ENABLE"] + if PYTHON3_ENABLE != "TRUE": + PYTHON3_ENABLE = "FALSE" + EdkLogger.quiet("%-16s = %s" % ("PYTHON3_ENABLE", PYTHON3_ENABLE)) + if "PYTHON_COMMAND" in os.environ: + EdkLogger.quiet("%-16s = %s" % ("PYTHON_COMMAND", os.environ["PYTHON_COMMAND"])) + self.InitPreBuild() + self.InitPostBuild() + if self.Prebuild: + EdkLogger.quiet("%-16s = %s" % ("PREBUILD", self.Prebuild)) + if self.Postbuild: + EdkLogger.quiet("%-16s = %s" % ("POSTBUILD", self.Postbuild)) + if self.Prebuild: + self.LaunchPrebuild() + TargetObj = TargetTxtDict() + ToolDefObj = ToolDefDict((os.path.join(os.getenv("WORKSPACE"), "Conf"))) + self.TargetTxt = TargetObj.Target + self.ToolDef = ToolDefObj.ToolDef + if not (self.LaunchPrebuildFlag and os.path.exists(self.PlatformBuildPath)): + self.InitBuild() + + self.AutoGenMgr = None + EdkLogger.info("") + os.chdir(self.WorkspaceDir) + self.log_q = log_q + GlobalData.file_lock = mp.Lock() + # Init cache data for local only + GlobalData.gPackageHashFile = dict() + GlobalData.gModulePreMakeCacheStatus = dict() + GlobalData.gModuleMakeCacheStatus = dict() + GlobalData.gHashChainStatus = dict() + GlobalData.gCMakeHashFile = dict() + GlobalData.gModuleHashFile = dict() + GlobalData.gFileHashDict = dict() + GlobalData.gModuleAllCacheStatus = set() + GlobalData.gModuleCacheHit = set() + + def StartAutoGen(self,mqueue, DataPipe,SkipAutoGen,PcdMaList,cqueue): + try: + if SkipAutoGen: + return True,0 + feedback_q = mp.Queue() + error_event = mp.Event() + FfsCmd = DataPipe.Get("FfsCommand") + if FfsCmd is None: + FfsCmd = {} + GlobalData.FfsCmd = FfsCmd + auto_workers = [AutoGenWorkerInProcess(mqueue,DataPipe.dump_file,feedback_q,GlobalData.file_lock,cqueue,self.log_q,error_event) for _ in range(self.ThreadNumber)] + self.AutoGenMgr = AutoGenManager(auto_workers,feedback_q,error_event) + self.AutoGenMgr.start() + for w in auto_workers: + w.start() + if PcdMaList is not None: + for PcdMa in PcdMaList: + # SourceFileList calling sequence impact the makefile string sequence. + # Create cached SourceFileList here to unify its calling sequence for both + # CanSkipbyPreMakeCache and CreateCodeFile/CreateMakeFile. + RetVal = PcdMa.SourceFileList + # Force cache miss for PCD driver + if GlobalData.gUseHashCache and not GlobalData.gBinCacheDest and self.Target in [None, "", "all"]: + cqueue.put((PcdMa.MetaFile.Path, PcdMa.Arch, "PreMakeCache", False)) + + PcdMa.CreateCodeFile(False) + PcdMa.CreateMakeFile(False,GenFfsList = DataPipe.Get("FfsCommand").get((PcdMa.MetaFile.Path, PcdMa.Arch),[])) + PcdMa.CreateAsBuiltInf() + # Force cache miss for PCD driver + if GlobalData.gBinCacheSource and self.Target in [None, "", "all"]: + cqueue.put((PcdMa.MetaFile.Path, PcdMa.Arch, "MakeCache", False)) + + self.AutoGenMgr.join() + rt = self.AutoGenMgr.Status + err = 0 + if not rt: + err = UNKNOWN_ERROR + return rt, err + except FatalError as e: + return False, e.args[0] + except: + return False, UNKNOWN_ERROR + + ## Add TOOLCHAIN and FAMILY declared in DSC [BuildOptions] to ToolsDefTxtDatabase. + # + # Loop through the set of build targets, tool chains, and archs provided on either + # the command line or in target.txt to discover FAMILY and TOOLCHAIN delclarations + # in [BuildOptions] sections that may be within !if expressions that may use + # $(TARGET), $(TOOLCHAIN), $(TOOLCHAIN_TAG), or $(ARCH) operands. + # + def GetToolChainAndFamilyFromDsc (self, File): + SavedGlobalDefines = GlobalData.gGlobalDefines.copy() + for BuildTarget in self.BuildTargetList: + GlobalData.gGlobalDefines['TARGET'] = BuildTarget + for BuildToolChain in self.ToolChainList: + GlobalData.gGlobalDefines['TOOLCHAIN'] = BuildToolChain + GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = BuildToolChain + for BuildArch in self.ArchList: + GlobalData.gGlobalDefines['ARCH'] = BuildArch + dscobj = self.BuildDatabase[File, BuildArch] + for KeyFamily, Key, KeyCodeBase in dscobj.BuildOptions: + try: + Target, ToolChain, Arch, Tool, Attr = Key.split('_') + except: + continue + if ToolChain == TAB_STAR or Attr != TAB_TOD_DEFINES_FAMILY: + continue + try: + Family = dscobj.BuildOptions[(KeyFamily, Key, KeyCodeBase)] + Family = Family.strip().strip('=').strip() + except: + continue + if TAB_TOD_DEFINES_FAMILY not in self.ToolDef.ToolsDefTxtDatabase: + self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_FAMILY] = {} + if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_FAMILY]: + self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_FAMILY][ToolChain] = Family + if TAB_TOD_DEFINES_BUILDRULEFAMILY not in self.ToolDef.ToolsDefTxtDatabase: + self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_BUILDRULEFAMILY] = {} + if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_BUILDRULEFAMILY]: + self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_BUILDRULEFAMILY][ToolChain] = Family + if TAB_TOD_DEFINES_TOOL_CHAIN_TAG not in self.ToolDef.ToolsDefTxtDatabase: + self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG] = [] + if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]: + self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG].append(ToolChain) + GlobalData.gGlobalDefines = SavedGlobalDefines + + ## Load configuration + # + # This method will parse target.txt and get the build configurations. + # + def LoadConfiguration(self): + + # if no ARCH given in command line, get it from target.txt + if not self.ArchList: + self.ArchList = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TARGET_ARCH] + self.ArchList = tuple(self.ArchList) + + # if no build target given in command line, get it from target.txt + if not self.BuildTargetList: + self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TARGET] + + # if no tool chain given in command line, get it from target.txt + if not self.ToolChainList: + self.ToolChainList = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TOOL_CHAIN_TAG] + if self.ToolChainList is None or len(self.ToolChainList) == 0: + EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n") + + if not self.PlatformFile: + PlatformFile = self.TargetTxt.TargetTxtDictionary[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) + + self.GetToolChainAndFamilyFromDsc (self.PlatformFile) + + # 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 + + ToolChainFamily = [] + ToolDefinition = self.ToolDef.ToolsDefTxtDatabase + for Tool in self.ToolChainList: + if TAB_TOD_DEFINES_FAMILY not in ToolDefinition or Tool not in ToolDefinition[TAB_TOD_DEFINES_FAMILY] \ + or not ToolDefinition[TAB_TOD_DEFINES_FAMILY][Tool]: + EdkLogger.warn("build", "No tool chain family found in configuration for %s. Default to MSFT." % Tool) + ToolChainFamily.append(TAB_COMPILER_MSFT) + else: + ToolChainFamily.append(ToolDefinition[TAB_TOD_DEFINES_FAMILY][Tool]) + self.ToolChainFamily = ToolChainFamily + + self.ThreadNumber = ThreadNum() + ## 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): + # parse target.txt, tools_def.txt, and platform file + self.LoadConfiguration() + + # Allow case-insensitive for those from command line or configuration file + ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False) + if ErrorCode != 0: + EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo) + + + def InitPreBuild(self): + self.LoadConfiguration() + ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False) + if ErrorCode != 0: + EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo) + if self.BuildTargetList: + GlobalData.gGlobalDefines['TARGET'] = self.BuildTargetList[0] + if self.ArchList: + GlobalData.gGlobalDefines['ARCH'] = self.ArchList[0] + if self.ToolChainList: + GlobalData.gGlobalDefines['TOOLCHAIN'] = self.ToolChainList[0] + GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = self.ToolChainList[0] + if self.ToolChainFamily: + GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[0] + if 'PREBUILD' in GlobalData.gCommandLineDefines: + self.Prebuild = GlobalData.gCommandLineDefines.get('PREBUILD') + else: + self.Db_Flag = True + Platform = self.Db.MapPlatform(str(self.PlatformFile)) + self.Prebuild = str(Platform.Prebuild) + if self.Prebuild: + PrebuildList = [] + # + # Evaluate all arguments and convert arguments that are WORKSPACE + # relative paths to absolute paths. Filter arguments that look like + # flags or do not follow the file/dir naming rules to avoid false + # positives on this conversion. + # + for Arg in self.Prebuild.split(): + # + # Do not modify Arg if it looks like a flag or an absolute file path + # + if Arg.startswith('-') or os.path.isabs(Arg): + PrebuildList.append(Arg) + continue + # + # Do not modify Arg if it does not look like a Workspace relative + # path that starts with a valid package directory name + # + if not Arg[0].isalpha() or os.path.dirname(Arg) == '': + PrebuildList.append(Arg) + continue + # + # If Arg looks like a WORKSPACE relative path, then convert to an + # absolute path and check to see if the file exists. + # + Temp = mws.join(self.WorkspaceDir, Arg) + if os.path.isfile(Temp): + Arg = Temp + PrebuildList.append(Arg) + self.Prebuild = ' '.join(PrebuildList) + self.Prebuild += self.PassCommandOption(self.BuildTargetList, self.ArchList, self.ToolChainList, self.PlatformFile, self.Target) + + def InitPostBuild(self): + if 'POSTBUILD' in GlobalData.gCommandLineDefines: + self.Postbuild = GlobalData.gCommandLineDefines.get('POSTBUILD') + else: + Platform = self.Db.MapPlatform(str(self.PlatformFile)) + self.Postbuild = str(Platform.Postbuild) + if self.Postbuild: + PostbuildList = [] + # + # Evaluate all arguments and convert arguments that are WORKSPACE + # relative paths to absolute paths. Filter arguments that look like + # flags or do not follow the file/dir naming rules to avoid false + # positives on this conversion. + # + for Arg in self.Postbuild.split(): + # + # Do not modify Arg if it looks like a flag or an absolute file path + # + if Arg.startswith('-') or os.path.isabs(Arg): + PostbuildList.append(Arg) + continue + # + # Do not modify Arg if it does not look like a Workspace relative + # path that starts with a valid package directory name + # + if not Arg[0].isalpha() or os.path.dirname(Arg) == '': + PostbuildList.append(Arg) + continue + # + # If Arg looks like a WORKSPACE relative path, then convert to an + # absolute path and check to see if the file exists. + # + Temp = mws.join(self.WorkspaceDir, Arg) + if os.path.isfile(Temp): + Arg = Temp + PostbuildList.append(Arg) + self.Postbuild = ' '.join(PostbuildList) + self.Postbuild += self.PassCommandOption(self.BuildTargetList, self.ArchList, self.ToolChainList, self.PlatformFile, self.Target) + + def PassCommandOption(self, BuildTarget, TargetArch, ToolChain, PlatformFile, Target): + BuildStr = '' + if GlobalData.gCommand and isinstance(GlobalData.gCommand, list): + BuildStr += ' ' + ' '.join(GlobalData.gCommand) + TargetFlag = False + ArchFlag = False + ToolChainFlag = False + PlatformFileFlag = False + + if GlobalData.gOptions and not GlobalData.gOptions.BuildTarget: + TargetFlag = True + if GlobalData.gOptions and not GlobalData.gOptions.TargetArch: + ArchFlag = True + if GlobalData.gOptions and not GlobalData.gOptions.ToolChain: + ToolChainFlag = True + if GlobalData.gOptions and not GlobalData.gOptions.PlatformFile: + PlatformFileFlag = True + + if TargetFlag and BuildTarget: + if isinstance(BuildTarget, list) or isinstance(BuildTarget, tuple): + BuildStr += ' -b ' + ' -b '.join(BuildTarget) + elif isinstance(BuildTarget, str): + BuildStr += ' -b ' + BuildTarget + if ArchFlag and TargetArch: + if isinstance(TargetArch, list) or isinstance(TargetArch, tuple): + BuildStr += ' -a ' + ' -a '.join(TargetArch) + elif isinstance(TargetArch, str): + BuildStr += ' -a ' + TargetArch + if ToolChainFlag and ToolChain: + if isinstance(ToolChain, list) or isinstance(ToolChain, tuple): + BuildStr += ' -t ' + ' -t '.join(ToolChain) + elif isinstance(ToolChain, str): + BuildStr += ' -t ' + ToolChain + if PlatformFileFlag and PlatformFile: + if isinstance(PlatformFile, list) or isinstance(PlatformFile, tuple): + BuildStr += ' -p ' + ' -p '.join(PlatformFile) + elif isinstance(PlatformFile, str): + BuildStr += ' -p' + PlatformFile + BuildStr += ' --conf=' + GlobalData.gConfDirectory + if Target: + BuildStr += ' ' + Target + + return BuildStr + + def LaunchPrebuild(self): + if self.Prebuild: + EdkLogger.info("\n- Prebuild Start -\n") + self.LaunchPrebuildFlag = True + # + # The purpose of .PrebuildEnv file is capture environment variable settings set by the prebuild script + # and preserve them for the rest of the main build step, because the child process environment will + # evaporate as soon as it exits, we cannot get it in build step. + # + PrebuildEnvFile = os.path.join(GlobalData.gConfDirectory, '.cache', '.PrebuildEnv') + if os.path.isfile(PrebuildEnvFile): + os.remove(PrebuildEnvFile) + if os.path.isfile(self.PlatformBuildPath): + os.remove(self.PlatformBuildPath) + if sys.platform == "win32": + args = ' && '.join((self.Prebuild, 'set > ' + PrebuildEnvFile)) + Process = Popen(args, stdout=PIPE, stderr=PIPE, shell=True) + else: + args = ' && '.join((self.Prebuild, 'env > ' + PrebuildEnvFile)) + Process = Popen(args, stdout=PIPE, stderr=PIPE, shell=True) + + # launch two threads to read the STDOUT and STDERR + EndOfProcedure = Event() + EndOfProcedure.clear() + if Process.stdout: + StdOutThread = Thread(target=ReadMessage, args=(Process.stdout, EdkLogger.info, EndOfProcedure)) + StdOutThread.name = "STDOUT-Redirector" + StdOutThread.daemon = False + StdOutThread.start() + + if Process.stderr: + StdErrThread = Thread(target=ReadMessage, args=(Process.stderr, EdkLogger.quiet, EndOfProcedure)) + StdErrThread.name = "STDERR-Redirector" + StdErrThread.daemon = False + StdErrThread.start() + # waiting for program exit + Process.wait() + + if Process.stdout: + StdOutThread.join() + if Process.stderr: + StdErrThread.join() + if Process.returncode != 0 : + EdkLogger.error("Prebuild", PREBUILD_ERROR, 'Prebuild process is not success!') + + if os.path.exists(PrebuildEnvFile): + f = open(PrebuildEnvFile) + envs = f.readlines() + f.close() + envs = [l.split("=", 1) for l in envs ] + envs = [[I.strip() for I in item] for item in envs if len(item) == 2] + os.environ.update(dict(envs)) + EdkLogger.info("\n- Prebuild Done -\n") + + def LaunchPostbuild(self): + if self.Postbuild: + EdkLogger.info("\n- Postbuild Start -\n") + if sys.platform == "win32": + Process = Popen(self.Postbuild, stdout=PIPE, stderr=PIPE, shell=True) + else: + Process = Popen(self.Postbuild, stdout=PIPE, stderr=PIPE, shell=True) + # launch two threads to read the STDOUT and STDERR + EndOfProcedure = Event() + EndOfProcedure.clear() + if Process.stdout: + StdOutThread = Thread(target=ReadMessage, args=(Process.stdout, EdkLogger.info, EndOfProcedure)) + StdOutThread.name = "STDOUT-Redirector" + StdOutThread.daemon = False + StdOutThread.start() + + if Process.stderr: + StdErrThread = Thread(target=ReadMessage, args=(Process.stderr, EdkLogger.quiet, EndOfProcedure)) + StdErrThread.name = "STDERR-Redirector" + StdErrThread.daemon = False + StdErrThread.start() + # waiting for program exit + Process.wait() + + if Process.stdout: + StdOutThread.join() + if Process.stderr: + StdErrThread.join() + if Process.returncode != 0 : + EdkLogger.error("Postbuild", POSTBUILD_ERROR, 'Postbuild process is not success!') + EdkLogger.info("\n- Postbuild Done -\n") + + ## Build a module or platform + # + # Create autogen code and makefile 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 _BuildPa(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True, BuildModule=False, FfsCommand=None, PcdMaList=None): + if AutoGenObject is None: + return False + if FfsCommand is None: + FfsCommand = {} + # 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 + mqueue = mp.Queue() + for m in AutoGenObject.GetAllModuleInfo: + mqueue.put(m) + mqueue.put((None,None,None,None,None,None,None)) + AutoGenObject.DataPipe.DataContainer = {"CommandTarget": self.Target} + AutoGenObject.DataPipe.DataContainer = {"Workspace_timestamp": AutoGenObject.Workspace._SrcTimeStamp} + AutoGenObject.CreateLibModuelDirs() + AutoGenObject.DataPipe.DataContainer = {"LibraryBuildDirectoryList":AutoGenObject.LibraryBuildDirectoryList} + AutoGenObject.DataPipe.DataContainer = {"ModuleBuildDirectoryList":AutoGenObject.ModuleBuildDirectoryList} + AutoGenObject.DataPipe.DataContainer = {"FdsCommandDict": AutoGenObject.Workspace.GenFdsCommandDict} + self.Progress.Start("Generating makefile and code") + data_pipe_file = os.path.join(AutoGenObject.BuildDir, "GlobalVar_%s_%s.bin" % (str(AutoGenObject.Guid),AutoGenObject.Arch)) + AutoGenObject.DataPipe.dump(data_pipe_file) + cqueue = mp.Queue() + autogen_rt,errorcode = self.StartAutoGen(mqueue, AutoGenObject.DataPipe, self.SkipAutoGen, PcdMaList, cqueue) + AutoGenIdFile = os.path.join(GlobalData.gConfDirectory,".AutoGenIdFile.txt") + with open(AutoGenIdFile,"w") as fw: + fw.write("Arch=%s\n" % "|".join((AutoGenObject.Workspace.ArchList))) + fw.write("BuildDir=%s\n" % AutoGenObject.Workspace.BuildDir) + fw.write("PlatformGuid=%s\n" % str(AutoGenObject.Guid)) + self.Progress.Stop("done!") + if not autogen_rt: + self.AutoGenMgr.TerminateWorkers() + self.AutoGenMgr.join(1) + raise FatalError(errorcode) + AutoGenObject.CreateCodeFile(False) + AutoGenObject.CreateMakeFile(False) + else: + # always recreate top/platform makefile when clean, just in case of inconsistency + AutoGenObject.CreateCodeFile(True) + AutoGenObject.CreateMakeFile(True) + + if EdkLogger.GetLevel() == EdkLogger.QUIET: + EdkLogger.quiet("Building ... %s" % repr(AutoGenObject)) + + BuildCommand = AutoGenObject.BuildCommand + if BuildCommand is None or len(BuildCommand) == 0: + EdkLogger.error("build", OPTION_MISSING, + "No build command found for this module. " + "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." % + (AutoGenObject.BuildTarget, AutoGenObject.ToolChain, AutoGenObject.Arch), + ExtraData=str(AutoGenObject)) + + # run + if Target == 'run': + return True + + # Fetch the MakeFileName. + self.MakeFileName = AutoGenObject.MakeFileName + + # build modules + if BuildModule: + BuildCommand = BuildCommand + [Target] + LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir) + if GlobalData.gBinCacheDest: + self.GenDestCache() + elif GlobalData.gUseHashCache and not GlobalData.gBinCacheSource: + # Only for --hash + # Update PreMakeCacheChain files + self.GenLocalPreMakeCache() + self.BuildModules = [] + return True + + # build library + if Target == 'libraries': + DirList = [] + for Lib in AutoGenObject.LibraryAutoGenList: + if not Lib.IsBinaryModule: + DirList.append((os.path.join(AutoGenObject.BuildDir, Lib.BuildDir),Lib)) + for Lib, LibAutoGen in DirList: + NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Lib, self.MakeFileName)), 'pbuild'] + LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir,LibAutoGen) + return True + + # build module + if Target == 'modules': + DirList = [] + for Lib in AutoGenObject.LibraryAutoGenList: + if not Lib.IsBinaryModule: + DirList.append((os.path.join(AutoGenObject.BuildDir, Lib.BuildDir),Lib)) + for Lib, LibAutoGen in DirList: + NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Lib, self.MakeFileName)), 'pbuild'] + LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir,LibAutoGen) + + DirList = [] + for ModuleAutoGen in AutoGenObject.ModuleAutoGenList: + if not ModuleAutoGen.IsBinaryModule: + DirList.append((os.path.join(AutoGenObject.BuildDir, ModuleAutoGen.BuildDir),ModuleAutoGen)) + for Mod,ModAutoGen in DirList: + NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Mod, self.MakeFileName)), 'pbuild'] + LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir,ModAutoGen) + self.CreateAsBuiltInf() + if GlobalData.gBinCacheDest: + self.GenDestCache() + elif GlobalData.gUseHashCache and not GlobalData.gBinCacheSource: + # Only for --hash + # Update PreMakeCacheChain files + self.GenLocalPreMakeCache() + self.BuildModules = [] + return True + + # cleanlib + if Target == 'cleanlib': + for Lib in AutoGenObject.LibraryBuildDirectoryList: + LibMakefile = os.path.normpath(os.path.join(Lib, self.MakeFileName)) + if os.path.exists(LibMakefile): + NewBuildCommand = BuildCommand + ['-f', LibMakefile, 'cleanall'] + LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir) + return True + + # clean + if Target == 'clean': + for Mod in AutoGenObject.ModuleBuildDirectoryList: + ModMakefile = os.path.normpath(os.path.join(Mod, self.MakeFileName)) + if os.path.exists(ModMakefile): + NewBuildCommand = BuildCommand + ['-f', ModMakefile, 'cleanall'] + LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir) + for Lib in AutoGenObject.LibraryBuildDirectoryList: + LibMakefile = os.path.normpath(os.path.join(Lib, self.MakeFileName)) + if os.path.exists(LibMakefile): + NewBuildCommand = BuildCommand + ['-f', LibMakefile, 'cleanall'] + LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir) + return True + + # cleanall + if Target == 'cleanall': + try: + #os.rmdir(AutoGenObject.BuildDir) + RemoveDirectory(AutoGenObject.BuildDir, True) + except WindowsError as X: + EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X)) + return True + + ## Build a module or platform + # + # Create autogen code and makefile 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, BuildModule=False): + if AutoGenObject is 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) + #AutoGenObject.CreateAsBuiltInf() + self.Progress.Stop("done!") + if Target == "genmake": + return True + else: + # always recreate top/platform makefile when clean, just in case of inconsistency + AutoGenObject.CreateCodeFile(True) + AutoGenObject.CreateMakeFile(True) + + if EdkLogger.GetLevel() == EdkLogger.QUIET: + EdkLogger.quiet("Building ... %s" % repr(AutoGenObject)) + + BuildCommand = AutoGenObject.BuildCommand + if BuildCommand is None or len(BuildCommand) == 0: + EdkLogger.error("build", OPTION_MISSING, + "No build command found for this module. " + "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." % + (AutoGenObject.BuildTarget, AutoGenObject.ToolChain, AutoGenObject.Arch), + ExtraData=str(AutoGenObject)) + + # build modules + if BuildModule: + if Target != 'fds': + BuildCommand = BuildCommand + [Target] + AutoGenObject.BuildTime = LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir) + self.CreateAsBuiltInf() + if GlobalData.gBinCacheDest: + self.GenDestCache() + elif GlobalData.gUseHashCache and not GlobalData.gBinCacheSource: + # Only for --hash + # Update PreMakeCacheChain files + self.GenLocalPreMakeCache() + self.BuildModules = [] + return True + + # genfds + if Target == 'fds': + if GenFdsApi(AutoGenObject.GenFdsCommandDict, self.Db): + EdkLogger.error("build", COMMAND_FAILURE) + Threshold = self.GetFreeSizeThreshold() + if Threshold: + self.CheckFreeSizeThreshold(Threshold, AutoGenObject.FvDir) + return True + + # run + if Target == 'run': + return True + + # build library + if Target == 'libraries': + pass + + # not build modules + + + # cleanall + if Target == 'cleanall': + try: + #os.rmdir(AutoGenObject.BuildDir) + RemoveDirectory(AutoGenObject.BuildDir, True) + except WindowsError as X: + EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X)) + return True + + ## Rebase module image and Get function address for the input module list. + # + def _RebaseModule (self, MapBuffer, BaseAddress, ModuleList, AddrIsOffset = True, ModeIsSmm = False): + if ModeIsSmm: + AddrIsOffset = False + for InfFile in ModuleList: + sys.stdout.write (".") + sys.stdout.flush() + ModuleInfo = ModuleList[InfFile] + ModuleName = ModuleInfo.BaseName + ModuleOutputImage = ModuleInfo.Image.FileName + ModuleDebugImage = os.path.join(ModuleInfo.DebugDir, ModuleInfo.BaseName + '.efi') + ## 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", ModuleOutputImage], ModuleInfo.OutputDir) + LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir) + else: + # + # Set new address to the section header only for SMM driver. + # + LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleOutputImage], ModuleInfo.OutputDir) + LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir) + # + # Collect function address from Map file + # + ImageMapTable = ModuleOutputImage.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)) + + ImageMap.close() + # + # Add general information. + # + if ModeIsSmm: + MapBuffer.append('\n\n%s (Fixed SMRAM Offset, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint)) + elif AddrIsOffset: + MapBuffer.append('\n\n%s (Fixed Memory Offset, BaseAddress=-0x%010X, EntryPoint=-0x%010X)\n' % (ModuleName, 0 - BaseAddress, 0 - (BaseAddress + ModuleInfo.Image.EntryPoint))) + else: + MapBuffer.append('\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.append('(GUID=%s, .textbaseaddress=-0x%010X, .databaseaddress=-0x%010X)\n' % (ModuleInfo.Guid, 0 - (BaseAddress + TextSectionAddress), 0 - (BaseAddress + DataSectionAddress))) + else: + MapBuffer.append('(GUID=%s, .textbaseaddress=0x%010X, .databaseaddress=0x%010X)\n' % (ModuleInfo.Guid, BaseAddress + TextSectionAddress, BaseAddress + DataSectionAddress)) + # + # Add debug image full path. + # + MapBuffer.append('(IMAGE=%s)\n\n' % (ModuleDebugImage)) + # + # Add function address + # + for Function in FunctionList: + if AddrIsOffset: + MapBuffer.append(' -0x%010X %s\n' % (0 - (BaseAddress + Function[1]), Function[0])) + else: + MapBuffer.append(' 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, ModuleList): + if self.Fdf: + # First get the XIP base address for FV map file. + GuidPattern = re.compile("[-a-fA-F0-9]+") + GuidName = re.compile(r"\(GUID=[-a-fA-F0-9]+") + for FvName in Wa.FdfProfile.FvDict: + 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() + for Line in FvMap: + MatchGuid = GuidPattern.match(Line) + if MatchGuid is not None: + # + # Replace GUID with module name + # + GuidString = MatchGuid.group() + if GuidString.upper() in ModuleList: + Line = Line.replace(GuidString, ModuleList[GuidString.upper()].Name) + MapBuffer.append(Line) + # + # Add the debug image full path. + # + MatchGuid = GuidName.match(Line) + if MatchGuid is not None: + GuidString = MatchGuid.group().split("=")[1] + if GuidString.upper() in ModuleList: + MapBuffer.append('(IMAGE=%s)\n' % (os.path.join(ModuleList[GuidString.upper()].DebugDir, ModuleList[GuidString.upper()].Name + '.efi'))) + + 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 + for ModuleGuid in ModuleList: + Module = ModuleList[ModuleGuid] + 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, Module.DebugDir, ImageClass) + if Module.ModuleType in [SUP_MODULE_PEI_CORE, SUP_MODULE_PEIM, EDK_COMPONENT_TYPE_COMBINED_PEIM_DRIVER, EDK_COMPONENT_TYPE_PIC_PEIM, EDK_COMPONENT_TYPE_RELOCATABLE_PEIM, SUP_MODULE_DXE_CORE]: + PeiModuleList[Module.MetaFile] = ImageInfo + PeiSize += ImageInfo.Image.Size + elif Module.ModuleType in [EDK_COMPONENT_TYPE_BS_DRIVER, SUP_MODULE_DXE_DRIVER, SUP_MODULE_UEFI_DRIVER]: + BtModuleList[Module.MetaFile] = ImageInfo + BtSize += ImageInfo.Image.Size + elif Module.ModuleType in [SUP_MODULE_DXE_RUNTIME_DRIVER, EDK_COMPONENT_TYPE_RT_DRIVER, SUP_MODULE_DXE_SAL_DRIVER, EDK_COMPONENT_TYPE_SAL_RT_DRIVER]: + RtModuleList[Module.MetaFile] = ImageInfo + RtSize += ImageInfo.Image.Size + elif Module.ModuleType in [SUP_MODULE_SMM_CORE, SUP_MODULE_DXE_SMM_DRIVER, SUP_MODULE_MM_STANDALONE, SUP_MODULE_MM_CORE_STANDALONE]: + SmmModuleList[Module.MetaFile] = ImageInfo + SmmSize += ImageInfo.Image.Size + if Module.ModuleType == SUP_MODULE_DXE_SMM_DRIVER: + PiSpecVersion = Module.Module.Specification.get('PI_SPECIFICATION_VERSION', '0x00000000') + # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver. + if int(PiSpecVersion, 16) < 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_SET: + 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_SET: + 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") + + # + # 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.append('PEI_CODE_PAGE_NUMBER = 0x%x\n' % (PeiSize // 0x1000)) + MapBuffer.append('BOOT_CODE_PAGE_NUMBER = 0x%x\n' % (BtSize // 0x1000)) + MapBuffer.append('RUNTIME_CODE_PAGE_NUMBER = 0x%x\n' % (RtSize // 0x1000)) + if len (SmmModuleList) > 0: + MapBuffer.append('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.append('\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, ''.join(MapBuffer), False) + if self.LoadFixAddress != 0: + sys.stdout.write ("\nLoad Module At Fix Address Map file can be found at %s\n" % (MapFilePath)) + sys.stdout.flush() + + ## Build active platform for different build targets and different tool chains + # + def _BuildPlatform(self): + SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False) + for BuildTarget in self.BuildTargetList: + GlobalData.gGlobalDefines['TARGET'] = BuildTarget + index = 0 + for ToolChain in self.ToolChainList: + GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain + GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain + GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index] + index += 1 + Wa = WorkspaceAutoGen( + self.WorkspaceDir, + self.PlatformFile, + BuildTarget, + ToolChain, + self.ArchList, + self.BuildDatabase, + self.TargetTxt, + self.ToolDef, + self.Fdf, + self.FdList, + self.FvList, + self.CapList, + self.SkuId, + self.UniFlag, + self.Progress + ) + self.Fdf = Wa.FdfFile + self.LoadFixAddress = Wa.Platform.LoadFixAddress + self.BuildReport.AddPlatformReport(Wa) + self.Progress.Stop("done!") + + # Add ffs build to makefile + CmdListDict = {} + if GlobalData.gEnableGenfdsMultiThread and self.Fdf: + CmdListDict = self._GenFfsCmd(Wa.ArchList) + + for Arch in Wa.ArchList: + PcdMaList = [] + GlobalData.gGlobalDefines['ARCH'] = Arch + Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch) + 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,Pa.DataPipe) + if Ma is None: + continue + if Ma.PcdIsDriver: + Ma.PlatformInfo = Pa + Ma.Workspace = Wa + PcdMaList.append(Ma) + self.BuildModules.append(Ma) + Pa.DataPipe.DataContainer = {"FfsCommand":CmdListDict} + Pa.DataPipe.DataContainer = {"Workspace_timestamp": Wa._SrcTimeStamp} + self._BuildPa(self.Target, Pa, FfsCommand=CmdListDict,PcdMaList=PcdMaList) + + # Create MAP file when Load Fix Address is enabled. + if self.Target in ["", "all", "fds"]: + for Arch in Wa.ArchList: + GlobalData.gGlobalDefines['ARCH'] = Arch + # + # 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 platform with IA32 or ARM arch modules") + # + # Get Module List + # + ModuleList = {} + for Pa in Wa.AutoGenObjectList: + for Ma in Pa.ModuleAutoGenList: + if Ma is None: + continue + if not Ma.IsLibrary: + ModuleList[Ma.Guid.upper()] = Ma + + MapBuffer = [] + if self.LoadFixAddress != 0: + # + # 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, ModuleList) + # + # Save MAP buffer into MAP file. + # + self._SaveMapFile (MapBuffer, Wa) + self.CreateGuidedSectionToolsFile(Wa) + + ## Build active module for different build targets, different tool chains and different archs + # + def _BuildModule(self): + for BuildTarget in self.BuildTargetList: + GlobalData.gGlobalDefines['TARGET'] = BuildTarget + index = 0 + for ToolChain in self.ToolChainList: + WorkspaceAutoGenTime = time.time() + GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain + GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain + GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index] + index += 1 + # + # module build needs platform build information, so get platform + # AutoGen first + # + Wa = WorkspaceAutoGen( + self.WorkspaceDir, + self.PlatformFile, + BuildTarget, + ToolChain, + self.ArchList, + self.BuildDatabase, + self.TargetTxt, + self.ToolDef, + self.Fdf, + self.FdList, + self.FvList, + self.CapList, + self.SkuId, + self.UniFlag, + self.Progress, + self.ModuleFile + ) + self.Fdf = Wa.FdfFile + self.LoadFixAddress = Wa.Platform.LoadFixAddress + Wa.CreateMakeFile(False) + # Add ffs build to makefile + CmdListDict = None + if GlobalData.gEnableGenfdsMultiThread and self.Fdf: + CmdListDict = self._GenFfsCmd(Wa.ArchList) + + GlobalData.file_lock = mp.Lock() + GlobalData.FfsCmd = CmdListDict + + self.Progress.Stop("done!") + MaList = [] + ExitFlag = threading.Event() + ExitFlag.clear() + self.AutoGenTime += int(round((time.time() - WorkspaceAutoGenTime))) + for Arch in Wa.ArchList: + AutoGenStart = time.time() + GlobalData.gGlobalDefines['ARCH'] = Arch + Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch) + for Module in Pa.Platform.Modules: + if self.ModuleFile.Dir == Module.Dir and self.ModuleFile.Name == Module.Name: + Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile,Pa.DataPipe) + if Ma is None: + continue + if Ma.PcdIsDriver: + Ma.PlatformInfo = Pa + Ma.Workspace = Wa + MaList.append(Ma) + + if GlobalData.gUseHashCache and not GlobalData.gBinCacheDest and self.Target in [None, "", "all"]: + if Ma.CanSkipbyPreMakeCache(): + continue + else: + self.PreMakeCacheMiss.add(Ma) + + # 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': + self.Progress.Start("Generating code") + Ma.CreateCodeFile(True) + self.Progress.Stop("done!") + if self.Target == "genc": + return True + if not self.SkipAutoGen or self.Target == 'genmake': + self.Progress.Start("Generating makefile") + if CmdListDict and self.Fdf and (Module.Path, Arch) in CmdListDict: + Ma.CreateMakeFile(True, CmdListDict[Module.Path, Arch]) + del CmdListDict[Module.Path, Arch] + else: + Ma.CreateMakeFile(True) + self.Progress.Stop("done!") + if self.Target == "genmake": + return True + + if GlobalData.gBinCacheSource and self.Target in [None, "", "all"]: + if Ma.CanSkipbyMakeCache(): + continue + else: + self.MakeCacheMiss.add(Ma) + + self.BuildModules.append(Ma) + self.AutoGenTime += int(round((time.time() - AutoGenStart))) + MakeStart = time.time() + for Ma in self.BuildModules: + if not Ma.IsBinaryModule: + Bt = BuildTask.New(ModuleMakeUnit(Ma, Pa.BuildCommand,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) + self.MakeTime += int(round((time.time() - MakeStart))) + + MakeContiue = time.time() + ExitFlag.set() + BuildTask.WaitForComplete() + self.CreateAsBuiltInf() + if GlobalData.gBinCacheDest: + self.GenDestCache() + elif GlobalData.gUseHashCache and not GlobalData.gBinCacheSource: + # Only for --hash + # Update PreMakeCacheChain files + self.GenLocalPreMakeCache() + self.BuildModules = [] + self.MakeTime += int(round((time.time() - MakeContiue))) + if BuildTask.HasError(): + EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule) + + self.BuildReport.AddPlatformReport(Wa, MaList) + 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(Wa.ArchList), self.PlatformFile), + ExtraData=self.ModuleFile + ) + # Create MAP file when Load Fix Address is enabled. + if self.Target == "fds" and self.Fdf: + for Arch in Wa.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 is None: + continue + if not Ma.IsLibrary: + ModuleList[Ma.Guid.upper()] = Ma + + MapBuffer = [] + if self.LoadFixAddress != 0: + # + # Rebase module to the preferred memory address before GenFds + # + self._CollectModuleMapBuffer(MapBuffer, ModuleList) + # + # create FDS again for the updated EFI image + # + GenFdsStart = time.time() + self._Build("fds", Wa) + self.GenFdsTime += int(round((time.time() - GenFdsStart))) + # + # Create MAP file for all platform FVs after GenFds. + # + self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList) + # + # Save MAP buffer into MAP file. + # + self._SaveMapFile (MapBuffer, Wa) + + def _GenFfsCmd(self,ArchList): + # convert dictionary of Cmd:(Inf,Arch) + # to a new dictionary of (Inf,Arch):Cmd,Cmd,Cmd... + CmdSetDict = defaultdict(set) + GenFfsDict = GenFds.GenFfsMakefile('', GlobalData.gFdfParser, self, ArchList, GlobalData) + for Cmd in GenFfsDict: + tmpInf, tmpArch = GenFfsDict[Cmd] + CmdSetDict[tmpInf, tmpArch].add(Cmd) + return CmdSetDict + def VerifyAutoGenFiles(self): + AutoGenIdFile = os.path.join(GlobalData.gConfDirectory,".AutoGenIdFile.txt") + try: + with open(AutoGenIdFile) as fd: + lines = fd.readlines() + except: + return None + for line in lines: + if "Arch" in line: + ArchList = line.strip().split("=")[1].split("|") + if "BuildDir" in line: + BuildDir = line.split("=")[1].strip() + if "PlatformGuid" in line: + PlatformGuid = line.split("=")[1].strip() + GlobalVarList = [] + for arch in ArchList: + global_var = os.path.join(BuildDir, "GlobalVar_%s_%s.bin" % (str(PlatformGuid),arch)) + if not os.path.exists(global_var): + return None + GlobalVarList.append(global_var) + for global_var in GlobalVarList: + data_pipe = MemoryDataPipe() + data_pipe.load(global_var) + target = data_pipe.Get("P_Info").get("Target") + toolchain = data_pipe.Get("P_Info").get("ToolChain") + archlist = data_pipe.Get("P_Info").get("ArchList") + Arch = data_pipe.Get("P_Info").get("Arch") + active_p = data_pipe.Get("P_Info").get("ActivePlatform") + workspacedir = data_pipe.Get("P_Info").get("WorkspaceDir") + PackagesPath = os.getenv("PACKAGES_PATH") + mws.setWs(workspacedir, PackagesPath) + LibraryBuildDirectoryList = data_pipe.Get("LibraryBuildDirectoryList") + ModuleBuildDirectoryList = data_pipe.Get("ModuleBuildDirectoryList") + + for m_build_dir in LibraryBuildDirectoryList: + if not os.path.exists(os.path.join(m_build_dir,self.MakeFileName)): + return None + for m_build_dir in ModuleBuildDirectoryList: + if not os.path.exists(os.path.join(m_build_dir,self.MakeFileName)): + return None + Wa = WorkSpaceInfo( + workspacedir,active_p,target,toolchain,archlist + ) + Pa = PlatformInfo(Wa, active_p, target, toolchain, Arch,data_pipe) + Wa.AutoGenObjectList.append(Pa) + return Wa + def SetupMakeSetting(self,Wa): + BuildModules = [] + for Pa in Wa.AutoGenObjectList: + for m in Pa._MbList: + ma = ModuleAutoGen(Wa,m.MetaFile, Pa.BuildTarget, Wa.ToolChain, Pa.Arch, Pa.MetaFile,Pa.DataPipe) + BuildModules.append(ma) + fdf_file = Wa.FlashDefinition + if fdf_file: + Fdf = FdfParser(fdf_file.Path) + Fdf.ParseFile() + GlobalData.gFdfParser = Fdf + if Fdf.CurrentFdName and Fdf.CurrentFdName in Fdf.Profile.FdDict: + FdDict = Fdf.Profile.FdDict[Fdf.CurrentFdName] + for FdRegion in FdDict.RegionList: + if str(FdRegion.RegionType) == 'FILE' and self.Platform.VpdToolGuid in str(FdRegion.RegionDataList): + if int(FdRegion.Offset) % 8 != 0: + EdkLogger.error("build", FORMAT_INVALID, 'The VPD Base Address %s must be 8-byte aligned.' % (FdRegion.Offset)) + Wa.FdfProfile = Fdf.Profile + self.Fdf = Fdf + else: + self.Fdf = None + return BuildModules + + ## Build a platform in multi-thread mode + # + def PerformAutoGen(self,BuildTarget,ToolChain): + WorkspaceAutoGenTime = time.time() + Wa = WorkspaceAutoGen( + self.WorkspaceDir, + self.PlatformFile, + BuildTarget, + ToolChain, + self.ArchList, + self.BuildDatabase, + self.TargetTxt, + self.ToolDef, + self.Fdf, + self.FdList, + self.FvList, + self.CapList, + self.SkuId, + self.UniFlag, + self.Progress + ) + self.Fdf = Wa.FdfFile + self.LoadFixAddress = Wa.Platform.LoadFixAddress + self.BuildReport.AddPlatformReport(Wa) + Wa.CreateMakeFile(False) + + # Add ffs build to makefile + CmdListDict = {} + if GlobalData.gEnableGenfdsMultiThread and self.Fdf: + CmdListDict = self._GenFfsCmd(Wa.ArchList) + + self.AutoGenTime += int(round((time.time() - WorkspaceAutoGenTime))) + BuildModules = [] + for Arch in Wa.ArchList: + PcdMaList = [] + AutoGenStart = time.time() + GlobalData.gGlobalDefines['ARCH'] = Arch + Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch) + if Pa is None: + continue + ModuleList = [] + for Inf in Pa.Platform.Modules: + ModuleList.append(Inf) + # Add the INF only list in FDF + if GlobalData.gFdfParser is not None: + for InfName in GlobalData.gFdfParser.Profile.InfList: + Inf = PathClass(NormPath(InfName), self.WorkspaceDir, Arch) + if Inf in Pa.Platform.Modules: + continue + ModuleList.append(Inf) + Pa.DataPipe.DataContainer = {"FfsCommand":CmdListDict} + Pa.DataPipe.DataContainer = {"Workspace_timestamp": Wa._SrcTimeStamp} + Pa.DataPipe.DataContainer = {"CommandTarget": self.Target} + Pa.CreateLibModuelDirs() + # Fetch the MakeFileName. + self.MakeFileName = Pa.MakeFileName + + Pa.DataPipe.DataContainer = {"LibraryBuildDirectoryList":Pa.LibraryBuildDirectoryList} + Pa.DataPipe.DataContainer = {"ModuleBuildDirectoryList":Pa.ModuleBuildDirectoryList} + Pa.DataPipe.DataContainer = {"FdsCommandDict": Wa.GenFdsCommandDict} + # Prepare the cache share data for multiprocessing + Pa.DataPipe.DataContainer = {"gPlatformHashFile":GlobalData.gPlatformHashFile} + ModuleCodaFile = {} + for ma in Pa.ModuleAutoGenList: + ModuleCodaFile[(ma.MetaFile.File,ma.MetaFile.Root,ma.Arch,ma.MetaFile.Path)] = [item.Target for item in ma.CodaTargetList] + Pa.DataPipe.DataContainer = {"ModuleCodaFile":ModuleCodaFile} + # ModuleList contains all driver modules only + for Module in ModuleList: + # Get ModuleAutoGen object to generate C code file and makefile + Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile,Pa.DataPipe) + if Ma is None: + continue + if Ma.PcdIsDriver: + Ma.PlatformInfo = Pa + Ma.Workspace = Wa + PcdMaList.append(Ma) + self.AllDrivers.add(Ma) + self.AllModules.add(Ma) + + mqueue = mp.Queue() + cqueue = mp.Queue() + for m in Pa.GetAllModuleInfo: + mqueue.put(m) + module_file,module_root,module_path,module_basename,\ + module_originalpath,module_arch,IsLib = m + Ma = ModuleAutoGen(Wa, PathClass(module_path, Wa), BuildTarget,\ + ToolChain, Arch, self.PlatformFile,Pa.DataPipe) + self.AllModules.add(Ma) + data_pipe_file = os.path.join(Pa.BuildDir, "GlobalVar_%s_%s.bin" % (str(Pa.Guid),Pa.Arch)) + Pa.DataPipe.dump(data_pipe_file) + + mqueue.put((None,None,None,None,None,None,None)) + autogen_rt, errorcode = self.StartAutoGen(mqueue, Pa.DataPipe, self.SkipAutoGen, PcdMaList, cqueue) + + if not autogen_rt: + self.AutoGenMgr.TerminateWorkers() + self.AutoGenMgr.join(1) + raise FatalError(errorcode) + + if GlobalData.gUseHashCache: + for item in GlobalData.gModuleAllCacheStatus: + (MetaFilePath, Arch, CacheStr, Status) = item + Ma = ModuleAutoGen(Wa, PathClass(MetaFilePath, Wa), BuildTarget,\ + ToolChain, Arch, self.PlatformFile,Pa.DataPipe) + if CacheStr == "PreMakeCache" and Status == False: + self.PreMakeCacheMiss.add(Ma) + if CacheStr == "PreMakeCache" and Status == True: + self.PreMakeCacheHit.add(Ma) + GlobalData.gModuleCacheHit.add(Ma) + if CacheStr == "MakeCache" and Status == False: + self.MakeCacheMiss.add(Ma) + if CacheStr == "MakeCache" and Status == True: + self.MakeCacheHit.add(Ma) + GlobalData.gModuleCacheHit.add(Ma) + self.AutoGenTime += int(round((time.time() - AutoGenStart))) + AutoGenIdFile = os.path.join(GlobalData.gConfDirectory,".AutoGenIdFile.txt") + with open(AutoGenIdFile,"w") as fw: + fw.write("Arch=%s\n" % "|".join((Wa.ArchList))) + fw.write("BuildDir=%s\n" % Wa.BuildDir) + fw.write("PlatformGuid=%s\n" % str(Wa.AutoGenObjectList[0].Guid)) + + if GlobalData.gBinCacheSource: + BuildModules.extend(self.MakeCacheMiss) + elif GlobalData.gUseHashCache and not GlobalData.gBinCacheDest: + BuildModules.extend(self.PreMakeCacheMiss) + else: + BuildModules.extend(self.AllDrivers) + + self.Progress.Stop("done!") + return Wa, BuildModules + + def _MultiThreadBuildPlatform(self): + SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False) + for BuildTarget in self.BuildTargetList: + GlobalData.gGlobalDefines['TARGET'] = BuildTarget + index = 0 + for ToolChain in self.ToolChainList: + resetFdsGlobalVariable() + GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain + GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain + GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index] + index += 1 + ExitFlag = threading.Event() + ExitFlag.clear() + if self.SkipAutoGen: + Wa = self.VerifyAutoGenFiles() + if Wa is None: + self.SkipAutoGen = False + Wa, self.BuildModules = self.PerformAutoGen(BuildTarget,ToolChain) + else: + GlobalData.gAutoGenPhase = True + self.BuildModules = self.SetupMakeSetting(Wa) + else: + Wa, self.BuildModules = self.PerformAutoGen(BuildTarget,ToolChain) + Pa = Wa.AutoGenObjectList[0] + GlobalData.gAutoGenPhase = False + + if GlobalData.gBinCacheSource: + EdkLogger.quiet("[cache Summary]: Total module num: %s" % len(self.AllModules)) + EdkLogger.quiet("[cache Summary]: PreMakecache miss num: %s " % len(self.PreMakeCacheMiss)) + EdkLogger.quiet("[cache Summary]: Makecache miss num: %s " % len(self.MakeCacheMiss)) + + for Arch in Wa.ArchList: + MakeStart = time.time() + for Ma in set(self.BuildModules): + # Generate build task for the module + if not Ma.IsBinaryModule: + Bt = BuildTask.New(ModuleMakeUnit(Ma, Pa.BuildCommand,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 + + if BuildTask.HasError(): + EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule) + self.MakeTime += int(round((time.time() - MakeStart))) + + MakeContiue = time.time() + # + # + # All modules have been put in build tasks queue. Tell task scheduler + # to exit if all tasks are completed + # + ExitFlag.set() + BuildTask.WaitForComplete() + if GlobalData.gBinCacheDest: + self.GenDestCache() + elif GlobalData.gUseHashCache and not GlobalData.gBinCacheSource: + # Only for --hash + # Update PreMakeCacheChain files + self.GenLocalPreMakeCache() + # + # Get Module List + # + ModuleList = {ma.Guid.upper(): ma for ma in self.BuildModules} + self.BuildModules = [] + self.MakeTime += int(round((time.time() - MakeContiue))) + # + # 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"]: + for Arch in Wa.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") + + # + # Rebase module to the preferred memory address before GenFds + # + MapBuffer = [] + if self.LoadFixAddress != 0: + self._CollectModuleMapBuffer(MapBuffer, ModuleList) + + if self.Fdf: + # + # Generate FD image if there's a FDF file found + # + GenFdsStart = time.time() + if GenFdsApi(Wa.GenFdsCommandDict, self.Db): + EdkLogger.error("build", COMMAND_FAILURE) + Threshold = self.GetFreeSizeThreshold() + if Threshold: + self.CheckFreeSizeThreshold(Threshold, Wa.FvDir) + + # + # Create MAP file for all platform FVs after GenFds. + # + self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList) + self.GenFdsTime += int(round((time.time() - GenFdsStart))) + # + # Save MAP buffer into MAP file. + # + self._SaveMapFile(MapBuffer, Wa) + self.CreateGuidedSectionToolsFile(Wa) + + ## GetFreeSizeThreshold() + # + # @retval int Threshold value + # + def GetFreeSizeThreshold(self): + Threshold = None + Threshold_Str = GlobalData.gCommandLineDefines.get('FV_SPARE_SPACE_THRESHOLD') + if Threshold_Str: + try: + if Threshold_Str.lower().startswith('0x'): + Threshold = int(Threshold_Str, 16) + else: + Threshold = int(Threshold_Str) + except: + EdkLogger.warn("build", 'incorrect value for FV_SPARE_SPACE_THRESHOLD %s.Only decimal or hex format is allowed.' % Threshold_Str) + return Threshold + + def CheckFreeSizeThreshold(self, Threshold=None, FvDir=None): + if not isinstance(Threshold, int): + return + if not isinstance(FvDir, str) or not FvDir: + return + FdfParserObject = GlobalData.gFdfParser + FvRegionNameList = [FvName for FvName in FdfParserObject.Profile.FvDict if FdfParserObject.Profile.FvDict[FvName].FvRegionInFD] + for FvName in FdfParserObject.Profile.FvDict: + if FvName in FvRegionNameList: + FvSpaceInfoFileName = os.path.join(FvDir, FvName.upper() + '.Fv.map') + if os.path.exists(FvSpaceInfoFileName): + FileLinesList = getlines(FvSpaceInfoFileName) + for Line in FileLinesList: + NameValue = Line.split('=') + if len(NameValue) == 2 and NameValue[0].strip() == 'EFI_FV_SPACE_SIZE': + FreeSizeValue = int(NameValue[1].strip(), 0) + if FreeSizeValue < Threshold: + EdkLogger.error("build", FV_FREESIZE_ERROR, + '%s FV free space %d is not enough to meet with the required spare space %d set by -D FV_SPARE_SPACE_THRESHOLD option.' % ( + FvName, FreeSizeValue, Threshold)) + break + + ## Generate GuidedSectionTools.txt in the FV directories. + # + def CreateGuidedSectionToolsFile(self,Wa): + for BuildTarget in self.BuildTargetList: + for ToolChain in self.ToolChainList: + FvDir = Wa.FvDir + if not os.path.exists(FvDir): + continue + for Arch in self.ArchList: + guidList = [] + tooldefguidList = [] + guidAttribs = [] + for Platform in Wa.AutoGenObjectList: + if Platform.BuildTarget != BuildTarget: + continue + if Platform.ToolChain != ToolChain: + continue + if Platform.Arch != Arch: + continue + if hasattr (Platform, 'BuildOption'): + for Tool in Platform.BuildOption: + if 'GUID' in Platform.BuildOption[Tool]: + if 'PATH' in Platform.BuildOption[Tool]: + value = Platform.BuildOption[Tool]['GUID'] + if value in guidList: + EdkLogger.error("build", FORMAT_INVALID, "Duplicate GUID value %s used with Tool %s in DSC [BuildOptions]." % (value, Tool)) + path = Platform.BuildOption[Tool]['PATH'] + guidList.append(value) + guidAttribs.append((value, Tool, path)) + for Tool in Platform.ToolDefinition: + if 'GUID' in Platform.ToolDefinition[Tool]: + if 'PATH' in Platform.ToolDefinition[Tool]: + value = Platform.ToolDefinition[Tool]['GUID'] + if value in tooldefguidList: + EdkLogger.error("build", FORMAT_INVALID, "Duplicate GUID value %s used with Tool %s in tools_def.txt." % (value, Tool)) + tooldefguidList.append(value) + if value in guidList: + # Already added by platform + continue + path = Platform.ToolDefinition[Tool]['PATH'] + guidList.append(value) + guidAttribs.append((value, Tool, path)) + # Sort by GuidTool name + guidAttribs = sorted (guidAttribs, key=lambda x: x[1]) + # Write out GuidedSecTools.txt + toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt') + toolsFile = open(toolsFile, 'wt') + for guidedSectionTool in guidAttribs: + print(' '.join(guidedSectionTool), file=toolsFile) + toolsFile.close() + + ## Returns the real path of the tool. + # + def GetRealPathOfTool (self, tool): + if os.path.exists(tool): + return os.path.realpath(tool) + return tool + + ## Launch the module or platform build + # + def Launch(self): + self.AllDrivers = set() + self.AllModules = set() + self.PreMakeCacheMiss = set() + self.PreMakeCacheHit = set() + self.MakeCacheMiss = set() + self.MakeCacheHit = set() + if not self.ModuleFile: + if not self.SpawnMode or self.Target not in ["", "all"]: + self.SpawnMode = False + self._BuildPlatform() + else: + self._MultiThreadBuildPlatform() + else: + self.SpawnMode = False + self._BuildModule() + + if self.Target == 'cleanall': + RemoveDirectory(os.path.dirname(GlobalData.gDatabasePath), True) + + def CreateAsBuiltInf(self): + for Module in self.BuildModules: + Module.CreateAsBuiltInf() + + def GenDestCache(self): + for Module in self.AllModules: + Module.GenPreMakefileHashList() + Module.GenMakefileHashList() + Module.CopyModuleToCache() + + def GenLocalPreMakeCache(self): + for Module in self.PreMakeCacheMiss: + Module.GenPreMakefileHashList() + + ## Do some clean-up works when error occurred + def Relinquish(self): + OldLogLevel = EdkLogger.GetLevel() + EdkLogger.SetLevel(EdkLogger.ERROR) + Utils.Progressor.Abort() + if self.SpawnMode == True: + BuildTask.Abort() + EdkLogger.SetLevel(OldLogLevel) + +def ParseDefines(DefineList=[]): + DefineDict = {} + if DefineList is not None: + for Define in DefineList: + DefineTokenList = Define.split("=", 1) + if not GlobalData.gMacroNamePattern.match(DefineTokenList[0]): + EdkLogger.error('build', FORMAT_INVALID, + "The macro name must be in the pattern [A-Z][A-Z0-9_]*", + ExtraData=DefineTokenList[0]) + + if len(DefineTokenList) == 1: + DefineDict[DefineTokenList[0]] = "TRUE" + else: + DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip() + return DefineDict + + + +def LogBuildTime(Time): + if Time: + TimeDurStr = '' + TimeDur = time.gmtime(Time) + if TimeDur.tm_yday > 1: + TimeDurStr = time.strftime("%H:%M:%S", TimeDur) + ", %d day(s)" % (TimeDur.tm_yday - 1) + else: + TimeDurStr = time.strftime("%H:%M:%S", TimeDur) + return TimeDurStr + else: + return None +def ThreadNum(): + OptionParser = MyOptionParser() + if not OptionParser.BuildOption and not OptionParser.BuildTarget: + OptionParser.GetOption() + BuildOption, BuildTarget = OptionParser.BuildOption, OptionParser.BuildTarget + ThreadNumber = BuildOption.ThreadNumber + GlobalData.gCmdConfDir = BuildOption.ConfDirectory + if ThreadNumber is None: + TargetObj = TargetTxtDict() + ThreadNumber = TargetObj.Target.TargetTxtDictionary[TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER] + if ThreadNumber == '': + ThreadNumber = 0 + else: + ThreadNumber = int(ThreadNumber, 0) + + if ThreadNumber == 0: + try: + ThreadNumber = multiprocessing.cpu_count() + except (ImportError, NotImplementedError): + ThreadNumber = 1 + return ThreadNumber +## 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 +# +LogQMaxSize = ThreadNum() * 10 +def Main(): + StartTime = time.time() + + # + # Create a log Queue + # + LogQ = mp.Queue(LogQMaxSize) + # Initialize log system + EdkLogger.LogClientInitialize(LogQ) + GlobalData.gCommand = sys.argv[1:] + # + # Parse the options and args + # + OptionParser = MyOptionParser() + if not OptionParser.BuildOption and not OptionParser.BuildTarget: + OptionParser.GetOption() + Option, Target = OptionParser.BuildOption, OptionParser.BuildTarget + GlobalData.gOptions = Option + GlobalData.gCaseInsensitive = Option.CaseInsensitive + + # Set log level + LogLevel = EdkLogger.INFO + if Option.verbose is not None: + EdkLogger.SetLevel(EdkLogger.VERBOSE) + LogLevel = EdkLogger.VERBOSE + elif Option.quiet is not None: + EdkLogger.SetLevel(EdkLogger.QUIET) + LogLevel = EdkLogger.QUIET + elif Option.debug is not None: + EdkLogger.SetLevel(Option.debug + 1) + LogLevel = Option.debug + 1 + else: + EdkLogger.SetLevel(EdkLogger.INFO) + + if Option.WarningAsError == True: + EdkLogger.SetWarningAsError() + Log_Agent = LogAgent(LogQ,LogLevel,Option.LogFile) + Log_Agent.start() + + if platform.platform().find("Windows") >= 0: + GlobalData.gIsWindows = True + else: + GlobalData.gIsWindows = False + + EdkLogger.quiet("Build environment: %s" % platform.platform()) + EdkLogger.quiet(time.strftime("Build start time: %H:%M:%S, %b.%d %Y\n", time.localtime())); + ReturnCode = 0 + MyBuild = None + BuildError = True + 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))) + + # + # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH + # + CheckEnvVariable() + GlobalData.gCommandLineDefines.update(ParseDefines(Option.Macros)) + + 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 is not 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) + + if Option.FdfFile is not 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) + + if Option.Flag is not None and Option.Flag not in ['-c', '-s']: + EdkLogger.error("build", OPTION_VALUE_INVALID, "UNI flag must be one of -c or -s") + + MyBuild = Build(Target, Workspace, Option,LogQ) + GlobalData.gCommandLineDefines['ARCH'] = ' '.join(MyBuild.ArchList) + if not (MyBuild.LaunchPrebuildFlag and os.path.exists(MyBuild.PlatformBuildPath)): + MyBuild.Launch() + + # + # All job done, no error found and no exception raised + # + BuildError = False + except FatalError as X: + if MyBuild is not None: + # for multi-thread build exits safely + MyBuild.Relinquish() + if Option is not None and Option.debug is not None: + EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc()) + ReturnCode = X.args[0] + except Warning as X: + # error from Fdf parser + if MyBuild is not None: + # for multi-thread build exits safely + MyBuild.Relinquish() + if Option is not None and Option.debug is not 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: + if MyBuild is not None: + + # for multi-thread build exits safely + MyBuild.Relinquish() + ReturnCode = ABORT_ERROR + if Option is not None and Option.debug is not None: + EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc()) + except: + if MyBuild is not 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 is not 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 %s for help, attaching following call stack trace!)\n" % MSG_EDKII_MAIL_ADDR, + RaiseError=False + ) + EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc()) + ReturnCode = CODE_ERROR + finally: + Utils.Progressor.Abort() + Utils.ClearDuplicatedInf() + + if ReturnCode == 0: + try: + MyBuild.LaunchPostbuild() + Conclusion = "Done" + except: + Conclusion = "Failed" + ReturnCode = POSTBUILD_ERROR + elif ReturnCode == ABORT_ERROR: + Conclusion = "Aborted" + else: + Conclusion = "Failed" + FinishTime = time.time() + BuildDuration = time.gmtime(int(round(FinishTime - StartTime))) + BuildDurationStr = "" + if BuildDuration.tm_yday > 1: + BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration) + ", %d day(s)" % (BuildDuration.tm_yday - 1) + else: + BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration) + if MyBuild is not None: + if not BuildError: + MyBuild.BuildReport.GenerateReport(BuildDurationStr, LogBuildTime(MyBuild.AutoGenTime), LogBuildTime(MyBuild.MakeTime), LogBuildTime(MyBuild.GenFdsTime)) + + EdkLogger.SetLevel(EdkLogger.QUIET) + EdkLogger.quiet("\n- %s -" % Conclusion) + EdkLogger.quiet(time.strftime("Build end time: %H:%M:%S, %b.%d %Y", time.localtime())) + EdkLogger.quiet("Build total time: %s\n" % BuildDurationStr) + Log_Agent.kill() + Log_Agent.join() + return ReturnCode + +if __name__ == '__main__': + try: + mp.set_start_method('spawn') + except: + pass + 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) diff --git a/BaseTools/Source/Python/build/buildoptions.py b/BaseTools/Source/Python/build/buildoptions.py index 5ec561f7ec..b4ce18e33f 100644 --- a/BaseTools/Source/Python/build/buildoptions.py +++ b/BaseTools/Source/Python/build/buildoptions.py @@ -1,106 +1,106 @@ -## @file
-# build a platform or a module
-#
-# Copyright (c) 2014, Hewlett-Packard Development Company, L.P.<BR>
-# Copyright (c) 2007 - 2021, Intel Corporation. All rights reserved.<BR>
-# Copyright (c) 2018 - 2020, Hewlett Packard Enterprise Development, L.P.<BR>
-# Copyright (c) 2022, Loongson Technology Corporation Limited. All rights reserved.<BR>
-#
-# SPDX-License-Identifier: BSD-2-Clause-Patent
-#
-
-# Version and Copyright
-from Common.BuildVersion import gBUILD_VERSION
-from optparse import OptionParser
-VersionNumber = "0.60" + ' ' + gBUILD_VERSION
-__version__ = "%prog Version " + VersionNumber
-__copyright__ = "Copyright (c) 2007 - 2018, Intel Corporation All rights reserved."
-
-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)
-
-
-class MyOptionParser():
-
- def __new__(cls, *args, **kw):
- if not hasattr(cls, '_instance'):
- orig = super(MyOptionParser, cls)
- cls._instance = orig.__new__(cls, *args, **kw)
- return cls._instance
-
- def __init__(self):
- if not hasattr(self, 'BuildOption'):
- self.BuildOption = None
- if not hasattr(self, 'BuildTarget'):
- self.BuildTarget = None
-
- def GetOption(self):
- 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", dest="TargetArch",
- help="ARCHS is one of list: IA32, X64, ARM, AARCH64, RISCV64, LOONGARCH64 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", type="string", dest="BuildTarget", help="Using the TARGET to build the platform, overriding target.txt's TARGET definition.",
- action="append")
- 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. When value is set to 0, tool automatically detect number of "\
- "processor threads, set value to 1 means disable multi-thread build, and set value to more than 1 means user specify the threads number to build.")
-
- 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("-C", "--capsule-image", action="append", type="string", dest="CapName", default=[],
- help="The name of Capsule to be generated. The name must be from [Capsule] 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", default=False, help="Don't check case of file name.")
-
- 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', 'HASH', 'EXECUTION_ORDER', 'COMPILE_INFO'], 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, HASH, EXECUTION_ORDER, COMPILE_INFO]. "\
- "To specify more than one flag, repeat this option on the command line and the default flag set is [PCD, LIBRARY, FLASH, DEPEX, HASH, BUILD_FLAGS, FIXED_ADDRESS]")
- Parser.add_option("-F", "--flag", action="store", type="string", dest="Flag",
- help="Specify the specific option to parse EDK UNI file. Must be one of: [-c, -s]. -c is for EDK framework UNI file, and -s is for EDK UEFI UNI file. "\
- "This option can also be specified by setting *_*_*_BUILD_FLAGS in [BuildOptions] section of platform DSC. If they are both specified, this value "\
- "will override the setting in [BuildOptions] section of platform DSC.")
- Parser.add_option("-N", "--no-cache", action="store_true", dest="DisableCache", default=False, help="Disable build cache mechanism")
- Parser.add_option("--conf", action="store", type="string", dest="ConfDirectory", help="Specify the customized Conf directory.")
- Parser.add_option("--check-usage", action="store_true", dest="CheckUsage", default=False, help="Check usage content of entries listed in INF file.")
- Parser.add_option("--ignore-sources", action="store_true", dest="IgnoreSources", default=False, help="Focus to a binary build and ignore all source files")
- Parser.add_option("--pcd", action="append", dest="OptionPcd", help="Set PCD value by command line. Format: \"PcdName=Value\" ")
- Parser.add_option("-l", "--cmd-len", action="store", type="int", dest="CommandLength", help="Specify the maximum line length of build command. Default is 4096.")
- Parser.add_option("--hash", action="store_true", dest="UseHashCache", default=False, help="Enable hash-based caching during build process.")
- Parser.add_option("--binary-destination", action="store", type="string", dest="BinCacheDest", help="Generate a cache of binary files in the specified directory.")
- Parser.add_option("--binary-source", action="store", type="string", dest="BinCacheSource", help="Consume a cache of binary files from the specified directory.")
- Parser.add_option("--genfds-multi-thread", action="store_true", dest="GenfdsMultiThread", default=True, help="Enable GenFds multi thread to generate ffs file.")
- Parser.add_option("--no-genfds-multi-thread", action="store_true", dest="NoGenfdsMultiThread", default=False, help="Disable GenFds multi thread to generate ffs file.")
- Parser.add_option("--disable-include-path-check", action="store_true", dest="DisableIncludePathCheck", default=False, help="Disable the include path check for outside of package.")
- self.BuildOption, self.BuildTarget = Parser.parse_args()
+## @file +# build a platform or a module +# +# Copyright (c) 2014, Hewlett-Packard Development Company, L.P.<BR> +# Copyright (c) 2007 - 2021, Intel Corporation. All rights reserved.<BR> +# Copyright (c) 2018 - 2020, Hewlett Packard Enterprise Development, L.P.<BR> +# Copyright (c) 2022, Loongson Technology Corporation Limited. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +# Version and Copyright +from Common.BuildVersion import gBUILD_VERSION +from optparse import OptionParser +VersionNumber = "0.60" + ' ' + gBUILD_VERSION +__version__ = "%prog Version " + VersionNumber +__copyright__ = "Copyright (c) 2007 - 2018, Intel Corporation All rights reserved." + +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) + + +class MyOptionParser(): + + def __new__(cls, *args, **kw): + if not hasattr(cls, '_instance'): + orig = super(MyOptionParser, cls) + cls._instance = orig.__new__(cls, *args, **kw) + return cls._instance + + def __init__(self): + if not hasattr(self, 'BuildOption'): + self.BuildOption = None + if not hasattr(self, 'BuildTarget'): + self.BuildTarget = None + + def GetOption(self): + 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", dest="TargetArch", + help="ARCHS is one of list: IA32, X64, ARM, AARCH64, RISCV64, LOONGARCH64 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", type="string", dest="BuildTarget", help="Using the TARGET to build the platform, overriding target.txt's TARGET definition.", + action="append") + 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. When value is set to 0, tool automatically detect number of "\ + "processor threads, set value to 1 means disable multi-thread build, and set value to more than 1 means user specify the threads number to build.") + + 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("-C", "--capsule-image", action="append", type="string", dest="CapName", default=[], + help="The name of Capsule to be generated. The name must be from [Capsule] 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", default=False, help="Don't check case of file name.") + + 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', 'HASH', 'EXECUTION_ORDER', 'COMPILE_INFO'], 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, HASH, EXECUTION_ORDER, COMPILE_INFO]. "\ + "To specify more than one flag, repeat this option on the command line and the default flag set is [PCD, LIBRARY, FLASH, DEPEX, HASH, BUILD_FLAGS, FIXED_ADDRESS]") + Parser.add_option("-F", "--flag", action="store", type="string", dest="Flag", + help="Specify the specific option to parse EDK UNI file. Must be one of: [-c, -s]. -c is for EDK framework UNI file, and -s is for EDK UEFI UNI file. "\ + "This option can also be specified by setting *_*_*_BUILD_FLAGS in [BuildOptions] section of platform DSC. If they are both specified, this value "\ + "will override the setting in [BuildOptions] section of platform DSC.") + Parser.add_option("-N", "--no-cache", action="store_true", dest="DisableCache", default=False, help="Disable build cache mechanism") + Parser.add_option("--conf", action="store", type="string", dest="ConfDirectory", help="Specify the customized Conf directory.") + Parser.add_option("--check-usage", action="store_true", dest="CheckUsage", default=False, help="Check usage content of entries listed in INF file.") + Parser.add_option("--ignore-sources", action="store_true", dest="IgnoreSources", default=False, help="Focus to a binary build and ignore all source files") + Parser.add_option("--pcd", action="append", dest="OptionPcd", help="Set PCD value by command line. Format: \"PcdName=Value\" ") + Parser.add_option("-l", "--cmd-len", action="store", type="int", dest="CommandLength", help="Specify the maximum line length of build command. Default is 4096.") + Parser.add_option("--hash", action="store_true", dest="UseHashCache", default=False, help="Enable hash-based caching during build process.") + Parser.add_option("--binary-destination", action="store", type="string", dest="BinCacheDest", help="Generate a cache of binary files in the specified directory.") + Parser.add_option("--binary-source", action="store", type="string", dest="BinCacheSource", help="Consume a cache of binary files from the specified directory.") + Parser.add_option("--genfds-multi-thread", action="store_true", dest="GenfdsMultiThread", default=True, help="Enable GenFds multi thread to generate ffs file.") + Parser.add_option("--no-genfds-multi-thread", action="store_true", dest="NoGenfdsMultiThread", default=False, help="Disable GenFds multi thread to generate ffs file.") + Parser.add_option("--disable-include-path-check", action="store_true", dest="DisableIncludePathCheck", default=False, help="Disable the include path check for outside of package.") + self.BuildOption, self.BuildTarget = Parser.parse_args() |
