text: ## @file
  # Global variables for GenFds
  #
  #  Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
  #
  #  This program and the accompanying materials
  #  are licensed and made available under the terms and conditions of the BSD License
  #  which accompanies this distribution.  The full text of the license may be found at
  #  http://opensource.org/licenses/bsd-license.php
  #
  #  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
  #  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
  #
  
  ##
  # Import Modules
  #
  from __future__ import print_function
  import Common.LongFilePathOs as os
  import sys
  import subprocess
  import struct
  import array
  
  from Common.BuildToolError import *
  from Common import EdkLogger
  from Common.Misc import SaveFileOnChange
  
  from Common.TargetTxtClassObject import TargetTxtClassObject
  from Common.ToolDefClassObject import ToolDefClassObject, ToolDefDict
  from AutoGen.BuildEngine import BuildRule
  import Common.DataType as DataType
  from Common.Misc import PathClass
  from Common.LongFilePathSupport import OpenLongFilePath as open
  from Common.MultipleWorkspace import MultipleWorkspace as mws
  
  ## Global variables
  #
  #
  class GenFdsGlobalVariable:
      FvDir = ''
      OutputDirDict = {}
      BinDir = ''
      # will be FvDir + os.sep + 'Ffs'
      FfsDir = ''
      FdfParser = None
      LibDir = ''
      WorkSpace = None
      WorkSpaceDir = ''
      ConfDir = ''
      EdkSourceDir = ''
      OutputDirFromDscDict = {}
      TargetName = ''
      ToolChainTag = ''
      RuleDict = {}
      ArchList = None
      VtfDict = {}
      ActivePlatform = None
      FvAddressFileName = ''
      VerboseMode = False
      DebugLevel = -1
      SharpCounter = 0
      SharpNumberPerLine = 40
      FdfFile = ''
      FdfFileTimeStamp = 0
      FixedLoadAddress = False
      PlatformName = ''
  
      BuildRuleFamily = "MSFT"
      ToolChainFamily = "MSFT"
      __BuildRuleDatabase = None
      GuidToolDefinition = {}
      FfsCmdDict = {}
      SecCmdList = []
      CopyList   = []
      ModuleFile = ''
      EnableGenfdsMultiThread = False
  
      #
      # The list whose element are flags to indicate if large FFS or SECTION files exist in FV.
      # At the beginning of each generation of FV, false flag is appended to the list,
      # after the call to GenerateSection returns, check the size of the output file,
      # if it is greater than 0xFFFFFF, the tail flag in list is set to true,
      # and EFI_FIRMWARE_FILE_SYSTEM3_GUID is passed to C GenFv.
      # At the end of generation of FV, pop the flag.
      # List is used as a stack to handle nested FV generation.
      #
      LargeFileInFvFlags = []
      EFI_FIRMWARE_FILE_SYSTEM3_GUID = '5473C07A-3DCB-4dca-BD6F-1E9689E7349A'
      LARGE_FILE_SIZE = 0x1000000
  
      SectionHeader = struct.Struct("3B 1B")
  
      # FvName, FdName, CapName in FDF, Image file name
      ImageBinDict = {}
  
      ## LoadBuildRule
      #
      @staticmethod
      def __LoadBuildRule():
          if GenFdsGlobalVariable.__BuildRuleDatabase:
              return GenFdsGlobalVariable.__BuildRuleDatabase
          BuildConfigurationFile = os.path.normpath(os.path.join(GenFdsGlobalVariable.ConfDir, "target.txt"))
          TargetTxt = TargetTxtClassObject()
          if os.path.isfile(BuildConfigurationFile) == True:
              TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)
              if DataType.TAB_TAT_DEFINES_BUILD_RULE_CONF in TargetTxt.TargetTxtDictionary:
                  BuildRuleFile = TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_BUILD_RULE_CONF]
              if not BuildRuleFile:
                  BuildRuleFile = 'Conf/build_rule.txt'
              GenFdsGlobalVariable.__BuildRuleDatabase = BuildRule(BuildRuleFile)
              ToolDefinitionFile = TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF]
              if ToolDefinitionFile == '':
                  ToolDefinitionFile = "Conf/tools_def.txt"
              if os.path.isfile(ToolDefinitionFile):
                  ToolDef = ToolDefClassObject()
                  ToolDef.LoadToolDefFile(ToolDefinitionFile)
                  ToolDefinition = ToolDef.ToolsDefTxtDatabase
                  if DataType.TAB_TOD_DEFINES_BUILDRULEFAMILY in ToolDefinition \
                     and GenFdsGlobalVariable.ToolChainTag in ToolDefinition[DataType.TAB_TOD_DEFINES_BUILDRULEFAMILY] \
                     and ToolDefinition[DataType.TAB_TOD_DEFINES_BUILDRULEFAMILY][GenFdsGlobalVariable.ToolChainTag]:
                      GenFdsGlobalVariable.BuildRuleFamily = ToolDefinition[DataType.TAB_TOD_DEFINES_BUILDRULEFAMILY][GenFdsGlobalVariable.ToolChainTag]
  
                  if DataType.TAB_TOD_DEFINES_FAMILY in ToolDefinition \
                     and GenFdsGlobalVariable.ToolChainTag in ToolDefinition[DataType.TAB_TOD_DEFINES_FAMILY] \
                     and ToolDefinition[DataType.TAB_TOD_DEFINES_FAMILY][GenFdsGlobalVariable.ToolChainTag]:
                      GenFdsGlobalVariable.ToolChainFamily = ToolDefinition[DataType.TAB_TOD_DEFINES_FAMILY][GenFdsGlobalVariable.ToolChainTag]
          return GenFdsGlobalVariable.__BuildRuleDatabase
  
      ## GetBuildRules
      #    @param Inf: object of InfBuildData
      #    @param Arch: current arch
      #
      @staticmethod
      def GetBuildRules(Inf, Arch):
          if not Arch:
              Arch = DataType.TAB_COMMON
  
          if not Arch in GenFdsGlobalVariable.OutputDirDict:
              return {}
  
          BuildRuleDatabase = GenFdsGlobalVariable.__LoadBuildRule()
          if not BuildRuleDatabase:
              return {}
  
          PathClassObj = PathClass(Inf.MetaFile.File,
                                   GenFdsGlobalVariable.WorkSpaceDir)
          Macro = {}
          Macro["WORKSPACE"             ] = GenFdsGlobalVariable.WorkSpaceDir
          Macro["MODULE_NAME"           ] = Inf.BaseName
          Macro["MODULE_GUID"           ] = Inf.Guid
          Macro["MODULE_VERSION"        ] = Inf.Version
          Macro["MODULE_TYPE"           ] = Inf.ModuleType
          Macro["MODULE_FILE"           ] = str(PathClassObj)
          Macro["MODULE_FILE_BASE_NAME" ] = PathClassObj.BaseName
          Macro["MODULE_RELATIVE_DIR"   ] = PathClassObj.SubDir
          Macro["MODULE_DIR"            ] = PathClassObj.SubDir
  
          Macro["BASE_NAME"             ] = Inf.BaseName
  
          Macro["ARCH"                  ] = Arch
          Macro["TOOLCHAIN"             ] = GenFdsGlobalVariable.ToolChainTag
          Macro["TOOLCHAIN_TAG"         ] = GenFdsGlobalVariable.ToolChainTag
          Macro["TOOL_CHAIN_TAG"        ] = GenFdsGlobalVariable.ToolChainTag
          Macro["TARGET"                ] = GenFdsGlobalVariable.TargetName
  
          Macro["BUILD_DIR"             ] = GenFdsGlobalVariable.OutputDirDict[Arch]
          Macro["BIN_DIR"               ] = os.path.join(GenFdsGlobalVariable.OutputDirDict[Arch], Arch)
          Macro["LIB_DIR"               ] = os.path.join(GenFdsGlobalVariable.OutputDirDict[Arch], Arch)
          BuildDir = os.path.join(
              GenFdsGlobalVariable.OutputDirDict[Arch],
              Arch,
              PathClassObj.SubDir,
              PathClassObj.BaseName
          )
          Macro["MODULE_BUILD_DIR"      ] = BuildDir
          Macro["OUTPUT_DIR"            ] = os.path.join(BuildDir, "OUTPUT")
          Macro["DEBUG_DIR"             ] = os.path.join(BuildDir, "DEBUG")
  
          BuildRules = {}
          for Type in BuildRuleDatabase.FileTypeList:
              #first try getting build rule by BuildRuleFamily
              RuleObject = BuildRuleDatabase[Type, Inf.BuildType, Arch, GenFdsGlobalVariable.BuildRuleFamily]
              if not RuleObject:
                  # build type is always module type, but ...
                  if Inf.ModuleType != Inf.BuildType:
                      RuleObject = BuildRuleDatabase[Type, Inf.ModuleType, Arch, GenFdsGlobalVariable.BuildRuleFamily]
              #second try getting build rule by ToolChainFamily
              if not RuleObject:
                  RuleObject = BuildRuleDatabase[Type, Inf.BuildType, Arch, GenFdsGlobalVariable.ToolChainFamily]
                  if not RuleObject:
                      # build type is always module type, but ...
                      if Inf.ModuleType != Inf.BuildType:
                          RuleObject = BuildRuleDatabase[Type, Inf.ModuleType, Arch, GenFdsGlobalVariable.ToolChainFamily]
              if not RuleObject:
                  continue
              RuleObject = RuleObject.Instantiate(Macro)
              BuildRules[Type] = RuleObject
              for Ext in RuleObject.SourceFileExtList:
                  BuildRules[Ext] = RuleObject
          return BuildRules
  
      ## GetModuleCodaTargetList
      #
      #    @param Inf: object of InfBuildData
      #    @param Arch: current arch
      #
      @staticmethod
      def GetModuleCodaTargetList(Inf, Arch):
          BuildRules = GenFdsGlobalVariable.GetBuildRules(Inf, Arch)
          if not BuildRules:
              return []
  
          TargetList = set()
          FileList = []
  
          if not Inf.IsBinaryModule:
              for File in Inf.Sources:
                  if File.TagName in ("", "*", GenFdsGlobalVariable.ToolChainTag) and \
                      File.ToolChainFamily in ("", "*", GenFdsGlobalVariable.ToolChainFamily):
                      FileList.append((File, DataType.TAB_UNKNOWN_FILE))
  
          for File in Inf.Binaries:
              if File.Target in [DataType.TAB_COMMON, '*', GenFdsGlobalVariable.TargetName]:
                  FileList.append((File, File.Type))
  
          for File, FileType in FileList:
              LastTarget = None
              RuleChain = []
              SourceList = [File]
              Index = 0
              while Index < len(SourceList):
                  Source = SourceList[Index]
                  Index = Index + 1
  
                  if File.IsBinary and File == Source and Inf.Binaries is not None and File in Inf.Binaries:
                      # Skip all files that are not binary libraries
                      if not Inf.LibraryClass:
                          continue
                      RuleObject = BuildRules[DataType.TAB_DEFAULT_BINARY_FILE]
                  elif FileType in BuildRules:
                      RuleObject = BuildRules[FileType]
                  elif Source.Ext in BuildRules:
                      RuleObject = BuildRules[Source.Ext]
                  else:
                      # stop at no more rules
                      if LastTarget:
                          TargetList.add(str(LastTarget))
                      break
  
                  FileType = RuleObject.SourceFileType
  
                  # stop at STATIC_LIBRARY for library
                  if Inf.LibraryClass and FileType == DataType.TAB_STATIC_LIBRARY:
                      if LastTarget:
                          TargetList.add(str(LastTarget))
                      break
  
                  Target = RuleObject.Apply(Source)
                  if not Target:
                      if LastTarget:
                          TargetList.add(str(LastTarget))
                      break
                  elif not Target.Outputs:
                      # Only do build for target with outputs
                      TargetList.add(str(Target))
  
                  # to avoid cyclic rule
                  if FileType in RuleChain:
                      break
  
                  RuleChain.append(FileType)
                  SourceList.extend(Target.Outputs)
                  LastTarget = Target
                  FileType = DataType.TAB_UNKNOWN_FILE
                  for Cmd in Target.Commands:
                      if "$(CP)" == Cmd.split()[0]:
                          CpTarget = Cmd.split()[2]
                          TargetList.add(CpTarget)
  
          return list(TargetList)
  
      ## SetDir()
      #
      #   @param  OutputDir           Output directory
      #   @param  FdfParser           FDF contents parser
      #   @param  Workspace           The directory of workspace
      #   @param  ArchList            The Arch list of platform
      #
      def SetDir (OutputDir, FdfParser, WorkSpace, ArchList):
          GenFdsGlobalVariable.VerboseLogger("GenFdsGlobalVariable.OutputDir :%s" % OutputDir)
  #        GenFdsGlobalVariable.OutputDirDict = OutputDir
          GenFdsGlobalVariable.FdfParser = FdfParser
          GenFdsGlobalVariable.WorkSpace = WorkSpace
          GenFdsGlobalVariable.FvDir = os.path.join(GenFdsGlobalVariable.OutputDirDict[ArchList[0]], DataType.TAB_FV_DIRECTORY)
          if not os.path.exists(GenFdsGlobalVariable.FvDir) :
              os.makedirs(GenFdsGlobalVariable.FvDir)
          GenFdsGlobalVariable.FfsDir = os.path.join(GenFdsGlobalVariable.FvDir, 'Ffs')
          if not os.path.exists(GenFdsGlobalVariable.FfsDir) :
              os.makedirs(GenFdsGlobalVariable.FfsDir)
  
          T_CHAR_LF = '\n'
          #
          # Create FV Address inf file
          #
          GenFdsGlobalVariable.FvAddressFileName = os.path.join(GenFdsGlobalVariable.FfsDir, 'FvAddress.inf')
          FvAddressFile = open(GenFdsGlobalVariable.FvAddressFileName, 'w')
          #
          # Add [Options]
          #
          FvAddressFile.writelines("[options]" + T_CHAR_LF)
          BsAddress = '0'
          for Arch in ArchList:
              if GenFdsGlobalVariable.WorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag].BsBaseAddress:
                  BsAddress = GenFdsGlobalVariable.WorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag].BsBaseAddress
                  break
  
          FvAddressFile.writelines("EFI_BOOT_DRIVER_BASE_ADDRESS = " + \
                                         BsAddress + \
                                         T_CHAR_LF)
  
          RtAddress = '0'
          for Arch in ArchList:
              if GenFdsGlobalVariable.WorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag].RtBaseAddress:
                  RtAddress = GenFdsGlobalVariable.WorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag].RtBaseAddress
  
          FvAddressFile.writelines("EFI_RUNTIME_DRIVER_BASE_ADDRESS = " + \
                                         RtAddress + \
                                         T_CHAR_LF)
  
          FvAddressFile.close()
  
      def SetEnv(FdfParser, WorkSpace, ArchList, GlobalData):
          GenFdsGlobalVariable.ModuleFile = WorkSpace.ModuleFile
          GenFdsGlobalVariable.FdfParser = FdfParser
          GenFdsGlobalVariable.WorkSpace = WorkSpace.Db
          GenFdsGlobalVariable.ArchList = ArchList
          GenFdsGlobalVariable.ToolChainTag = GlobalData.gGlobalDefines["TOOL_CHAIN_TAG"]
          GenFdsGlobalVariable.TargetName = GlobalData.gGlobalDefines["TARGET"]
          GenFdsGlobalVariable.ActivePlatform = GlobalData.gActivePlatform
          GenFdsGlobalVariable.EdkSourceDir = GlobalData.gGlobalDefines["EDK_SOURCE"]
          GenFdsGlobalVariable.ConfDir  = GlobalData.gConfDirectory
          GenFdsGlobalVariable.EnableGenfdsMultiThread = GlobalData.gEnableGenfdsMultiThread
          for Arch in ArchList:
              GenFdsGlobalVariable.OutputDirDict[Arch] = os.path.normpath(
                  os.path.join(GlobalData.gWorkspace,
                               WorkSpace.Db.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch, GlobalData.gGlobalDefines['TARGET'],
                               GlobalData.gGlobalDefines['TOOLCHAIN']].OutputDirectory,
                               GlobalData.gGlobalDefines['TARGET'] +'_' + GlobalData.gGlobalDefines['TOOLCHAIN']))
              GenFdsGlobalVariable.OutputDirFromDscDict[Arch] = os.path.normpath(
                               WorkSpace.Db.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch,
                               GlobalData.gGlobalDefines['TARGET'], GlobalData.gGlobalDefines['TOOLCHAIN']].OutputDirectory)
              GenFdsGlobalVariable.PlatformName = WorkSpace.Db.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch,
                                                                        GlobalData.gGlobalDefines['TARGET'],
                                                                        GlobalData.gGlobalDefines['TOOLCHAIN']].PlatformName
          GenFdsGlobalVariable.FvDir = os.path.join(GenFdsGlobalVariable.OutputDirDict[ArchList[0]], DataType.TAB_FV_DIRECTORY)
          if not os.path.exists(GenFdsGlobalVariable.FvDir):
              os.makedirs(GenFdsGlobalVariable.FvDir)
          GenFdsGlobalVariable.FfsDir = os.path.join(GenFdsGlobalVariable.FvDir, 'Ffs')
          if not os.path.exists(GenFdsGlobalVariable.FfsDir):
              os.makedirs(GenFdsGlobalVariable.FfsDir)
  
          T_CHAR_LF = '\n'
          #
          # Create FV Address inf file
          #
          GenFdsGlobalVariable.FvAddressFileName = os.path.join(GenFdsGlobalVariable.FfsDir, 'FvAddress.inf')
          FvAddressFile = open(GenFdsGlobalVariable.FvAddressFileName, 'w')
          #
          # Add [Options]
          #
          FvAddressFile.writelines("[options]" + T_CHAR_LF)
          BsAddress = '0'
          for Arch in ArchList:
              BsAddress = GenFdsGlobalVariable.WorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch,
                                                                     GlobalData.gGlobalDefines['TARGET'],
                                                                     GlobalData.gGlobalDefines["TOOL_CHAIN_TAG"]].BsBaseAddress
              if BsAddress:
                  break
  
          FvAddressFile.writelines("EFI_BOOT_DRIVER_BASE_ADDRESS = " + \
                                   BsAddress + \
                                   T_CHAR_LF)
  
          RtAddress = '0'
          for Arch in ArchList:
              if GenFdsGlobalVariable.WorkSpace.BuildObject[
                  GenFdsGlobalVariable.ActivePlatform, Arch, GlobalData.gGlobalDefines['TARGET'],
                  GlobalData.gGlobalDefines["TOOL_CHAIN_TAG"]].RtBaseAddress:
                  RtAddress = GenFdsGlobalVariable.WorkSpace.BuildObject[
                      GenFdsGlobalVariable.ActivePlatform, Arch, GlobalData.gGlobalDefines['TARGET'],
                      GlobalData.gGlobalDefines["TOOL_CHAIN_TAG"]].RtBaseAddress
  
          FvAddressFile.writelines("EFI_RUNTIME_DRIVER_BASE_ADDRESS = " + \
                                   RtAddress + \
                                   T_CHAR_LF)
  
          FvAddressFile.close()
  
      ## ReplaceWorkspaceMacro()
      #
      #   @param  String           String that may contain macro
      #
      def ReplaceWorkspaceMacro(String):
          String = mws.handleWsMacro(String)
          Str = String.replace('$(WORKSPACE)', GenFdsGlobalVariable.WorkSpaceDir)
          if os.path.exists(Str):
              if not os.path.isabs(Str):
                  Str = os.path.abspath(Str)
          else:
              Str = mws.join(GenFdsGlobalVariable.WorkSpaceDir, String)
          return os.path.normpath(Str)
  
      ## Check if the input files are newer than output files
      #
      #   @param  Output          Path of output file
      #   @param  Input           Path list of input files
      #
      #   @retval True            if Output doesn't exist, or any Input is newer
      #   @retval False           if all Input is older than Output
      #
      @staticmethod
      def NeedsUpdate(Output, Input):
          if not os.path.exists(Output):
              return True
          # always update "Output" if no "Input" given
          if Input is None or len(Input) == 0:
              return True
  
          # if fdf file is changed after the 'Output" is generated, update the 'Output'
          OutputTime = os.path.getmtime(Output)
          if GenFdsGlobalVariable.FdfFileTimeStamp > OutputTime:
              return True
  
          for F in Input:
              # always update "Output" if any "Input" doesn't exist
              if not os.path.exists(F):
                  return True
              # always update "Output" if any "Input" is newer than "Output"
              if os.path.getmtime(F) > OutputTime:
                  return True
          return False
  
      @staticmethod
      def GenerateSection(Output, Input, Type=None, CompressionType=None, Guid=None,
                          GuidHdrLen=None, GuidAttr=[], Ui=None, Ver=None, InputAlign=[], BuildNumber=None, DummyFile=None, IsMakefile=False):
          Cmd = ["GenSec"]
          if Type:
              Cmd += ("-s", Type)
          if CompressionType:
              Cmd += ("-c", CompressionType)
          if Guid is not None:
              Cmd += ("-g", Guid)
          if DummyFile is not None:
              Cmd += ("--dummy", DummyFile)
          if GuidHdrLen:
              Cmd += ("-l", GuidHdrLen)
          #Add each guided attribute
          for Attr in GuidAttr:
              Cmd += ("-r", Attr)
          #Section Align is only for dummy section without section type
          for SecAlign in InputAlign:
              Cmd += ("--sectionalign", SecAlign)
  
          CommandFile = Output + '.txt'
          if Ui:
              if IsMakefile:
                  if Ui == "$(MODULE_NAME)":
                      Cmd += ('-n', Ui)
                  else:
                      Cmd += ("-n", '"' + Ui + '"')
                  Cmd += ("-o", Output)
                  if ' '.join(Cmd).strip() not in GenFdsGlobalVariable.SecCmdList:
                      GenFdsGlobalVariable.SecCmdList.append(' '.join(Cmd).strip())
              else:
                  SectionData = array.array('B', [0, 0, 0, 0])
                  SectionData.fromstring(Ui.encode("utf_16_le"))
                  SectionData.append(0)
                  SectionData.append(0)
                  Len = len(SectionData)
                  GenFdsGlobalVariable.SectionHeader.pack_into(SectionData, 0, Len & 0xff, (Len >> 8) & 0xff, (Len >> 16) & 0xff, 0x15)
                  SaveFileOnChange(Output, SectionData.tostring())
  
          elif Ver:
              Cmd += ("-n", Ver)
              if BuildNumber:
                  Cmd += ("-j", BuildNumber)
              Cmd += ("-o", Output)
  
              SaveFileOnChange(CommandFile, ' '.join(Cmd), False)
              if IsMakefile:
                  if ' '.join(Cmd).strip() not in GenFdsGlobalVariable.SecCmdList:
                      GenFdsGlobalVariable.SecCmdList.append(' '.join(Cmd).strip())
              else:
                  if not GenFdsGlobalVariable.NeedsUpdate(Output, list(Input) + [CommandFile]):
                      return
                  GenFdsGlobalVariable.CallExternalTool(Cmd, "Failed to generate section")
          else:
              Cmd += ("-o", Output)
              Cmd += Input
  
              SaveFileOnChange(CommandFile, ' '.join(Cmd), False)
              if IsMakefile:
                  if ' '.join(Cmd).strip() not in GenFdsGlobalVariable.SecCmdList:
                      GenFdsGlobalVariable.SecCmdList.append(' '.join(Cmd).strip())
              elif GenFdsGlobalVariable.NeedsUpdate(Output, list(Input) + [CommandFile]):
                  GenFdsGlobalVariable.DebugLogger(EdkLogger.DEBUG_5, "%s needs update because of newer %s" % (Output, Input))
                  GenFdsGlobalVariable.CallExternalTool(Cmd, "Failed to generate section")
                  if (os.path.getsize(Output) >= GenFdsGlobalVariable.LARGE_FILE_SIZE and
                      GenFdsGlobalVariable.LargeFileInFvFlags):
                      GenFdsGlobalVariable.LargeFileInFvFlags[-1] = True
  
      @staticmethod
      def GetAlignment (AlignString):
          if AlignString is None:
              return 0
          if AlignString in ("1K", "2K", "4K", "8K", "16K", "32K", "64K", "128K", "256K", "512K"):
              return int (AlignString.rstrip('K')) * 1024
          elif AlignString in ("1M", "2M", "4M", "8M", "16M"):
              return int (AlignString.rstrip('M')) * 1024 * 1024
          else:
              return int (AlignString)
  
      @staticmethod
      def GenerateFfs(Output, Input, Type, Guid, Fixed=False, CheckSum=False, Align=None,
                      SectionAlign=None, MakefilePath=None):
          Cmd = ["GenFfs", "-t", Type, "-g", Guid]
          mFfsValidAlign = ["0", "8", "16", "128", "512", "1K", "4K", "32K", "64K", "128K", "256K", "512K", "1M", "2M", "4M", "8M", "16M"]
          if Fixed == True:
              Cmd.append("-x")
          if CheckSum:
              Cmd.append("-s")
          if Align:
              if Align not in mFfsValidAlign:
                  Align = GenFdsGlobalVariable.GetAlignment (Align)
                  for index in range(0, len(mFfsValidAlign) - 1):
                      if ((Align > GenFdsGlobalVariable.GetAlignment(mFfsValidAlign[index])) and (Align <= GenFdsGlobalVariable.GetAlignment(mFfsValidAlign[index + 1]))):
                          break
                  Align = mFfsValidAlign[index + 1]
              Cmd += ("-a", Align)
  
          Cmd += ("-o", Output)
          for I in range(0, len(Input)):
              Cmd += ("-i", Input[I])
              if SectionAlign and SectionAlign[I]:
                  Cmd += ("-n", SectionAlign[I])
  
          CommandFile = Output + '.txt'
          SaveFileOnChange(CommandFile, ' '.join(Cmd), False)
  
          GenFdsGlobalVariable.DebugLogger(EdkLogger.DEBUG_5, "%s needs update because of newer %s" % (Output, Input))
          if MakefilePath:
              if (tuple(Cmd), tuple(GenFdsGlobalVariable.SecCmdList), tuple(GenFdsGlobalVariable.CopyList)) not in GenFdsGlobalVariable.FfsCmdDict:
                  GenFdsGlobalVariable.FfsCmdDict[tuple(Cmd), tuple(GenFdsGlobalVariable.SecCmdList), tuple(GenFdsGlobalVariable.CopyList)] = MakefilePath
              GenFdsGlobalVariable.SecCmdList = []
              GenFdsGlobalVariable.CopyList = []
          else:
              if not GenFdsGlobalVariable.NeedsUpdate(Output, list(Input) + [CommandFile]):
                  return
              GenFdsGlobalVariable.CallExternalTool(Cmd, "Failed to generate FFS")
  
      @staticmethod
      def GenerateFirmwareVolume(Output, Input, BaseAddress=None, ForceRebase=None, Capsule=False, Dump=False,
                                 AddressFile=None, MapFile=None, FfsList=[], FileSystemGuid=None):
          if not GenFdsGlobalVariable.NeedsUpdate(Output, Input+FfsList):
              return
          GenFdsGlobalVariable.DebugLogger(EdkLogger.DEBUG_5, "%s needs update because of newer %s" % (Output, Input))
  
          Cmd = ["GenFv"]
          if BaseAddress:
              Cmd += ("-r", BaseAddress)
  
          if ForceRebase == False:
              Cmd += ("-F", "FALSE")
          elif ForceRebase == True:
              Cmd += ("-F", "TRUE")
  
          if Capsule:
              Cmd.append("-c")
          if Dump:
              Cmd.append("-p")
          if AddressFile:
              Cmd += ("-a", AddressFile)
          if MapFile:
              Cmd += ("-m", MapFile)
          if FileSystemGuid:
              Cmd += ("-g", FileSystemGuid)
          Cmd += ("-o", Output)
          for I in Input:
              Cmd += ("-i", I)
  
          GenFdsGlobalVariable.CallExternalTool(Cmd, "Failed to generate FV")
  
      @staticmethod
      def GenerateVtf(Output, Input, BaseAddress=None, FvSize=None):
          if not GenFdsGlobalVariable.NeedsUpdate(Output, Input):
              return
          GenFdsGlobalVariable.DebugLogger(EdkLogger.DEBUG_5, "%s needs update because of newer %s" % (Output, Input))
  
          Cmd = ["GenVtf"]
          if BaseAddress and FvSize \
              and len(BaseAddress) == len(FvSize):
              for I in range(0, len(BaseAddress)):
                  Cmd += ("-r", BaseAddress[I], "-s", FvSize[I])
          Cmd += ("-o", Output)
          for F in Input:
              Cmd += ("-f", F)
  
          GenFdsGlobalVariable.CallExternalTool(Cmd, "Failed to generate VTF")
  
      @staticmethod
      def GenerateFirmwareImage(Output, Input, Type="efi", SubType=None, Zero=False,
                                Strip=False, Replace=False, TimeStamp=None, Join=False,
                                Align=None, Padding=None, Convert=False, IsMakefile=False):
          if not GenFdsGlobalVariable.NeedsUpdate(Output, Input) and not IsMakefile:
              return
          GenFdsGlobalVariable.DebugLogger(EdkLogger.DEBUG_5, "%s needs update because of newer %s" % (Output, Input))
  
          Cmd = ["GenFw"]
          if Type.lower() == "te":
              Cmd.append("-t")
          if SubType:
              Cmd += ("-e", SubType)
          if TimeStamp:
              Cmd += ("-s", TimeStamp)
          if Align:
              Cmd += ("-a", Align)
          if Padding:
              Cmd += ("-p", Padding)
          if Zero:
              Cmd.append("-z")
          if Strip:
              Cmd.append("-l")
          if Replace:
              Cmd.append("-r")
          if Join:
              Cmd.append("-j")
          if Convert:
              Cmd.append("-m")
          Cmd += ("-o", Output)
          Cmd += Input
          if IsMakefile:
              if " ".join(Cmd).strip() not in GenFdsGlobalVariable.SecCmdList:
                  GenFdsGlobalVariable.SecCmdList.append(" ".join(Cmd).strip())
          else:
              GenFdsGlobalVariable.CallExternalTool(Cmd, "Failed to generate firmware image")
  
      @staticmethod
      def GenerateOptionRom(Output, EfiInput, BinaryInput, Compress=False, ClassCode=None,
                          Revision=None, DeviceId=None, VendorId=None, IsMakefile=False):
          InputList = []
          Cmd = ["EfiRom"]
          if len(EfiInput) > 0:
  
              if Compress:
                  Cmd.append("-ec")
              else:
                  Cmd.append("-e")
  
              for EfiFile in EfiInput:
                  Cmd.append(EfiFile)
                  InputList.append (EfiFile)
  
          if len(BinaryInput) > 0:
              Cmd.append("-b")
              for BinFile in BinaryInput:
                  Cmd.append(BinFile)
                  InputList.append (BinFile)
  
          # Check List
          if not GenFdsGlobalVariable.NeedsUpdate(Output, InputList) and not IsMakefile:
              return
          GenFdsGlobalVariable.DebugLogger(EdkLogger.DEBUG_5, "%s needs update because of newer %s" % (Output, InputList))
  
          if ClassCode is not None:
              Cmd += ("-l", ClassCode)
          if Revision is not None:
              Cmd += ("-r", Revision)
          if DeviceId is not None:
              Cmd += ("-i", DeviceId)
          if VendorId is not None:
              Cmd += ("-f", VendorId)
  
          Cmd += ("-o", Output)
          if IsMakefile:
              if " ".join(Cmd).strip() not in GenFdsGlobalVariable.SecCmdList:
                  GenFdsGlobalVariable.SecCmdList.append(" ".join(Cmd).strip())
          else:
              GenFdsGlobalVariable.CallExternalTool(Cmd, "Failed to generate option rom")
  
      @staticmethod
      def GuidTool(Output, Input, ToolPath, Options='', returnValue=[], IsMakefile=False):
          if not GenFdsGlobalVariable.NeedsUpdate(Output, Input) and not IsMakefile:
              return
          GenFdsGlobalVariable.DebugLogger(EdkLogger.DEBUG_5, "%s needs update because of newer %s" % (Output, Input))
  
          Cmd = [ToolPath, ]
          Cmd += Options.split(' ')
          Cmd += ("-o", Output)
          Cmd += Input
          if IsMakefile:
              if " ".join(Cmd).strip() not in GenFdsGlobalVariable.SecCmdList:
                  GenFdsGlobalVariable.SecCmdList.append(" ".join(Cmd).strip())
          else:
              GenFdsGlobalVariable.CallExternalTool(Cmd, "Failed to call " + ToolPath, returnValue)
  
      def CallExternalTool (cmd, errorMess, returnValue=[]):
  
          if type(cmd) not in (tuple, list):
              GenFdsGlobalVariable.ErrorLogger("ToolError!  Invalid parameter type in call to CallExternalTool")
  
          if GenFdsGlobalVariable.DebugLevel != -1:
              cmd += ('--debug', str(GenFdsGlobalVariable.DebugLevel))
              GenFdsGlobalVariable.InfLogger (cmd)
  
          if GenFdsGlobalVariable.VerboseMode:
              cmd += ('-v',)
              GenFdsGlobalVariable.InfLogger (cmd)
          else:
              sys.stdout.write ('#')
              sys.stdout.flush()
              GenFdsGlobalVariable.SharpCounter = GenFdsGlobalVariable.SharpCounter + 1
              if GenFdsGlobalVariable.SharpCounter % GenFdsGlobalVariable.SharpNumberPerLine == 0:
                  sys.stdout.write('\n')
  
          try:
              PopenObject = subprocess.Popen(' '.join(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
          except Exception as X:
              EdkLogger.error("GenFds", COMMAND_FAILURE, ExtraData="%s: %s" % (str(X), cmd[0]))
          (out, error) = PopenObject.communicate()
  
          while PopenObject.returncode is None :
              PopenObject.wait()
          if returnValue != [] and returnValue[0] != 0:
              #get command return value
              returnValue[0] = PopenObject.returncode
              return
          if PopenObject.returncode != 0 or GenFdsGlobalVariable.VerboseMode or GenFdsGlobalVariable.DebugLevel != -1:
              GenFdsGlobalVariable.InfLogger ("Return Value = %d" % PopenObject.returncode)
              GenFdsGlobalVariable.InfLogger (out)
              GenFdsGlobalVariable.InfLogger (error)
              if PopenObject.returncode != 0:
                  print("###", cmd)
                  EdkLogger.error("GenFds", COMMAND_FAILURE, errorMess)
  
      def VerboseLogger (msg):
          EdkLogger.verbose(msg)
  
      def InfLogger (msg):
          EdkLogger.info(msg)
  
      def ErrorLogger (msg, File=None, Line=None, ExtraData=None):
          EdkLogger.error('GenFds', GENFDS_ERROR, msg, File, Line, ExtraData)
  
      def DebugLogger (Level, msg):
          EdkLogger.debug(Level, msg)
  
      ## ReplaceWorkspaceMacro()
      #
      #   @param  Str           String that may contain macro
      #   @param  MacroDict     Dictionary that contains macro value pair
      #
      def MacroExtend (Str, MacroDict={}, Arch=DataType.TAB_COMMON):
          if Str is None :
              return None
  
          Dict = {'$(WORKSPACE)'   : GenFdsGlobalVariable.WorkSpaceDir,
                  '$(EDK_SOURCE)'  : GenFdsGlobalVariable.EdkSourceDir,
  #                '$(OUTPUT_DIRECTORY)': GenFdsGlobalVariable.OutputDirFromDsc,
                  '$(TARGET)' : GenFdsGlobalVariable.TargetName,
                  '$(TOOL_CHAIN_TAG)' : GenFdsGlobalVariable.ToolChainTag,
                  '$(SPACE)' : ' '
                 }
          OutputDir = GenFdsGlobalVariable.OutputDirFromDscDict[GenFdsGlobalVariable.ArchList[0]]
          if Arch != DataType.TAB_COMMON and Arch in GenFdsGlobalVariable.ArchList:
              OutputDir = GenFdsGlobalVariable.OutputDirFromDscDict[Arch]
  
          Dict['$(OUTPUT_DIRECTORY)'] = OutputDir
  
          if MacroDict is not None  and len (MacroDict) != 0:
              Dict.update(MacroDict)
  
          for key in Dict:
              if Str.find(key) >= 0 :
                  Str = Str.replace (key, Dict[key])
  
          if Str.find('$(ARCH)') >= 0:
              if len(GenFdsGlobalVariable.ArchList) == 1:
                  Str = Str.replace('$(ARCH)', GenFdsGlobalVariable.ArchList[0])
              else:
                  EdkLogger.error("GenFds", GENFDS_ERROR, "No way to determine $(ARCH) for %s" % Str)
  
          return Str
  
      ## GetPcdValue()
      #
      #   @param  PcdPattern           pattern that labels a PCD.
      #
      def GetPcdValue (PcdPattern):
          if PcdPattern is None :
              return None
          PcdPair = PcdPattern.lstrip('PCD(').rstrip(')').strip().split('.')
          TokenSpace = PcdPair[0]
          TokenCName = PcdPair[1]
  
          PcdValue = ''
          for Arch in GenFdsGlobalVariable.ArchList:
              Platform = GenFdsGlobalVariable.WorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag]
              PcdDict = Platform.Pcds
              for Key in PcdDict:
                  PcdObj = PcdDict[Key]
                  if (PcdObj.TokenCName == TokenCName) and (PcdObj.TokenSpaceGuidCName == TokenSpace):
                      if PcdObj.Type != DataType.TAB_PCDS_FIXED_AT_BUILD:
                          EdkLogger.error("GenFds", GENFDS_ERROR, "%s is not FixedAtBuild type." % PcdPattern)
                      if PcdObj.DatumType != DataType.TAB_VOID:
                          EdkLogger.error("GenFds", GENFDS_ERROR, "%s is not VOID* datum type." % PcdPattern)
  
                      PcdValue = PcdObj.DefaultValue
                      return PcdValue
  
              for Package in GenFdsGlobalVariable.WorkSpace.GetPackageList(GenFdsGlobalVariable.ActivePlatform,
                                                                           Arch,
                                                                           GenFdsGlobalVariable.TargetName,
                                                                           GenFdsGlobalVariable.ToolChainTag):
                  PcdDict = Package.Pcds
                  for Key in PcdDict:
                      PcdObj = PcdDict[Key]
                      if (PcdObj.TokenCName == TokenCName) and (PcdObj.TokenSpaceGuidCName == TokenSpace):
                          if PcdObj.Type != DataType.TAB_PCDS_FIXED_AT_BUILD:
                              EdkLogger.error("GenFds", GENFDS_ERROR, "%s is not FixedAtBuild type." % PcdPattern)
                          if PcdObj.DatumType != DataType.TAB_VOID:
                              EdkLogger.error("GenFds", GENFDS_ERROR, "%s is not VOID* datum type." % PcdPattern)
  
                          PcdValue = PcdObj.DefaultValue
                          return PcdValue
  
          return PcdValue
  
      SetDir = staticmethod(SetDir)
      SetEnv = staticmethod(SetEnv)
      ReplaceWorkspaceMacro = staticmethod(ReplaceWorkspaceMacro)
      CallExternalTool = staticmethod(CallExternalTool)
      VerboseLogger = staticmethod(VerboseLogger)
      InfLogger = staticmethod(InfLogger)
      ErrorLogger = staticmethod(ErrorLogger)
      DebugLogger = staticmethod(DebugLogger)
      MacroExtend = staticmethod (MacroExtend)
      GetPcdValue = staticmethod(GetPcdValue)
  
  ## FindExtendTool()
  #
  #  Find location of tools to process data
  #
  #  @param  KeyStringList    Filter for inputs of section generation
  #  @param  CurrentArchList  Arch list
  #  @param  NameGuid         The Guid name
  #
  def FindExtendTool(KeyStringList, CurrentArchList, NameGuid):
      ToolDb = ToolDefDict(GenFdsGlobalVariable.ConfDir).ToolsDefTxtDatabase
      # if user not specify filter, try to deduce it from global data.
      if KeyStringList is None or KeyStringList == []:
          Target = GenFdsGlobalVariable.TargetName
          ToolChain = GenFdsGlobalVariable.ToolChainTag
          if ToolChain not in ToolDb['TOOL_CHAIN_TAG']:
              EdkLogger.error("GenFds", GENFDS_ERROR, "Can not find external tool because tool tag %s is not defined in tools_def.txt!" % ToolChain)
          KeyStringList = [Target + '_' + ToolChain + '_' + CurrentArchList[0]]
          for Arch in CurrentArchList:
              if Target + '_' + ToolChain + '_' + Arch not in KeyStringList:
                  KeyStringList.append(Target + '_' + ToolChain + '_' + Arch)
  
      if GenFdsGlobalVariable.GuidToolDefinition:
          if NameGuid in GenFdsGlobalVariable.GuidToolDefinition:
              return GenFdsGlobalVariable.GuidToolDefinition[NameGuid]
  
      ToolDefinition = ToolDefDict(GenFdsGlobalVariable.ConfDir).ToolsDefTxtDictionary
      ToolPathTmp = None
      ToolOption = None
      ToolPathKey = None
      ToolOptionKey = None
      KeyList = None
      for ToolDef in ToolDefinition.items():
          if NameGuid.lower() == ToolDef[1].lower() :
              KeyList = ToolDef[0].split('_')
              Key = KeyList[0] + \
                    '_' + \
                    KeyList[1] + \
                    '_' + \
                    KeyList[2]
              if Key in KeyStringList and KeyList[4] == DataType.TAB_GUID:
                  ToolPathKey   = Key + '_' + KeyList[3] + '_PATH'
                  ToolOptionKey = Key + '_' + KeyList[3] + '_FLAGS'
                  ToolPath = ToolDefinition.get(ToolPathKey)
                  ToolOption = ToolDefinition.get(ToolOptionKey)
                  if ToolPathTmp is None:
                      ToolPathTmp = ToolPath
                  else:
                      if ToolPathTmp != ToolPath:
                          EdkLogger.error("GenFds", GENFDS_ERROR, "Don't know which tool to use, %s or %s ?" % (ToolPathTmp, ToolPath))
  
      BuildOption = {}
      for Arch in CurrentArchList:
          Platform = GenFdsGlobalVariable.WorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag]
          # key is (ToolChainFamily, ToolChain, CodeBase)
          for item in Platform.BuildOptions:
              if '_PATH' in item[1] or '_FLAGS' in item[1] or '_GUID' in item[1]:
                  if not item[0] or (item[0] and GenFdsGlobalVariable.ToolChainFamily== item[0]):
                      if item[1] not in BuildOption:
                          BuildOption[item[1]] = Platform.BuildOptions[item]
          if BuildOption:
              ToolList = [DataType.TAB_TOD_DEFINES_TARGET, DataType.TAB_TOD_DEFINES_TOOL_CHAIN_TAG, DataType.TAB_TOD_DEFINES_TARGET_ARCH]
              for Index in range(2, -1, -1):
                  for Key in list(BuildOption.keys()):
                      List = Key.split('_')
                      if List[Index] == '*':
                          for String in ToolDb[ToolList[Index]]:
                              if String in [Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag]:
                                  List[Index] = String
                                  NewKey = '%s_%s_%s_%s_%s' % tuple(List)
                                  if NewKey not in BuildOption:
                                      BuildOption[NewKey] = BuildOption[Key]
                                      continue
                                  del BuildOption[Key]
                      elif List[Index] not in ToolDb[ToolList[Index]]:
                          del BuildOption[Key]
      if BuildOption:
          if not KeyList:
              for Op in BuildOption:
                  if NameGuid == BuildOption[Op]:
                      KeyList = Op.split('_')
                      Key = KeyList[0] + '_' + KeyList[1] +'_' + KeyList[2]
                      if Key in KeyStringList and KeyList[4] == DataType.TAB_GUID:
                          ToolPathKey   = Key + '_' + KeyList[3] + '_PATH'
                          ToolOptionKey = Key + '_' + KeyList[3] + '_FLAGS'
          if ToolPathKey in BuildOption:
              ToolPathTmp = BuildOption[ToolPathKey]
          if ToolOptionKey in BuildOption:
              ToolOption = BuildOption[ToolOptionKey]
  
      GenFdsGlobalVariable.GuidToolDefinition[NameGuid] = (ToolPathTmp, ToolOption)
      return ToolPathTmp, ToolOption
  