]> git.proxmox.com Git - mirror_edk2.git/blobdiff - BaseTools/Source/Python/build/build.py
BaseTools: Fixed build clean regression issue
[mirror_edk2.git] / BaseTools / Source / Python / build / build.py
old mode 100644 (file)
new mode 100755 (executable)
index 0270611..cf82c29
 # Import Modules\r
 #\r
 from __future__ import print_function\r
-import Common.LongFilePathOs as os\r
-import re\r
+from __future__ import absolute_import\r
+import os.path as path\r
 import sys\r
+import os\r
+import re\r
 import glob\r
 import time\r
 import platform\r
 import traceback\r
-import encodings.ascii\r
 import multiprocessing\r
-\r
-from struct import *\r
-from threading import *\r
+from threading import Thread,Event,BoundedSemaphore\r
 import threading\r
-from optparse import OptionParser\r
-from subprocess import *\r
+from subprocess import Popen,PIPE\r
+from collections import OrderedDict, defaultdict\r
+from Common.buildoptions import BuildOption,BuildTarget\r
+from AutoGen.PlatformAutoGen import PlatformAutoGen\r
+from AutoGen.ModuleAutoGen import ModuleAutoGen\r
+from AutoGen.WorkspaceAutoGen import WorkspaceAutoGen\r
+from AutoGen.AutoGenWorker import AutoGenWorkerInProcess,AutoGenManager,\\r
+    LogAgent\r
+from AutoGen import GenMake\r
 from Common import Misc as Utils\r
 \r
-from Common.LongFilePathSupport import OpenLongFilePath as open\r
-from Common.TargetTxtClassObject import TargetTxtClassObject\r
-from Common.ToolDefClassObject import ToolDefClassObject\r
-from Common.DataType import *\r
-from Common.BuildVersion import gBUILD_VERSION\r
-from AutoGen.AutoGen import *\r
-from Common.BuildToolError import *\r
-from Workspace.WorkspaceDatabase import WorkspaceDatabase\r
+from Common.TargetTxtClassObject import TargetTxt\r
+from Common.ToolDefClassObject import ToolDef\r
+from Common.Misc import PathClass,SaveFileOnChange,RemoveDirectory\r
+from Common.StringUtils import NormPath\r
 from Common.MultipleWorkspace import MultipleWorkspace as mws\r
+from Common.BuildToolError import *\r
+from Common.DataType import *\r
+import Common.EdkLogger as EdkLogger\r
+\r
+from Workspace.WorkspaceDatabase import BuildDB\r
 \r
 from BuildReport import BuildReport\r
-from GenPatchPcdTable.GenPatchPcdTable import *\r
-from PatchPcdValue.PatchPcdValue import *\r
+from GenPatchPcdTable.GenPatchPcdTable import PeImageClass,parsePcdInfoFromMapFile\r
+from PatchPcdValue.PatchPcdValue import PatchBinaryFile\r
 \r
-import Common.EdkLogger\r
 import Common.GlobalData as GlobalData\r
 from GenFds.GenFds import GenFds, GenFdsApi\r
+import multiprocessing as mp\r
+from multiprocessing import Manager\r
 \r
-from collections import OrderedDict, defaultdict\r
-\r
-# Version and Copyright\r
-VersionNumber = "0.60" + ' ' + gBUILD_VERSION\r
-__version__ = "%prog Version " + VersionNumber\r
-__copyright__ = "Copyright (c) 2007 - 2018, Intel Corporation  All rights reserved."\r
 \r
 ## standard targets of build command\r
 gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run']\r
@@ -340,9 +342,9 @@ class ModuleMakeUnit(BuildUnit):
     #   @param  Obj         The ModuleAutoGen object the build is working on\r
     #   @param  Target      The build target name, one of gSupportedTarget\r
     #\r
-    def __init__(self, Obj, Target):\r
-        Dependency = [ModuleMakeUnit(La, Target) for La in Obj.LibraryAutoGenList]\r
-        BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)\r
+    def __init__(self, Obj, BuildCommand,Target):\r
+        Dependency = [ModuleMakeUnit(La, BuildCommand,Target) for La in Obj.LibraryAutoGenList]\r
+        BuildUnit.__init__(self, Obj, BuildCommand, Target, Dependency, Obj.MakeFileDir)\r
         if Target in [None, "", "all"]:\r
             self.Target = "tbuild"\r
 \r
@@ -361,10 +363,10 @@ class PlatformMakeUnit(BuildUnit):
     #   @param  Obj         The PlatformAutoGen object the build is working on\r
     #   @param  Target      The build target name, one of gSupportedTarget\r
     #\r
-    def __init__(self, Obj, Target):\r
-        Dependency = [ModuleMakeUnit(Lib, Target) for Lib in self.BuildObject.LibraryAutoGenList]\r
-        Dependency.extend([ModuleMakeUnit(Mod, Target) for Mod in self.BuildObject.ModuleAutoGenList])\r
-        BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)\r
+    def __init__(self, Obj, BuildCommand, Target):\r
+        Dependency = [ModuleMakeUnit(Lib, BuildCommand, Target) for Lib in self.BuildObject.LibraryAutoGenList]\r
+        Dependency.extend([ModuleMakeUnit(Mod, BuildCommand,Target) for Mod in self.BuildObject.ModuleAutoGenList])\r
+        BuildUnit.__init__(self, Obj, BuildCommand, Target, Dependency, Obj.MakeFileDir)\r
 \r
 ## The class representing the task of a module build or platform build\r
 #\r
@@ -593,7 +595,7 @@ class BuildTask:
     #\r
     def AddDependency(self, Dependency):\r
         for Dep in Dependency:\r
-            if not Dep.BuildObject.IsBinaryModule and not Dep.BuildObject.CanSkipbyHash():\r
+            if not Dep.BuildObject.IsBinaryModule and not Dep.BuildObject.CanSkipbyCache(GlobalData.gCacheIR):\r
                 self.DependencyList.append(BuildTask.New(Dep))    # BuildTask list\r
 \r
     ## The thread wrapper of LaunchCommand function\r
@@ -625,8 +627,15 @@ class BuildTask:
             BuildTask._ErrorFlag.set()\r
             BuildTask._ErrorMessage = "%s broken\n    %s [%s]" % \\r
                                       (threading.currentThread().getName(), Command, WorkingDir)\r
-        if self.BuildItem.BuildObject in GlobalData.gModuleBuildTracking and not BuildTask._ErrorFlag.isSet():\r
-            GlobalData.gModuleBuildTracking[self.BuildItem.BuildObject] = True\r
+\r
+        # Set the value used by hash invalidation flow in GlobalData.gModuleBuildTracking to 'SUCCESS'\r
+        # If Module or Lib is being tracked, it did not fail header check test, and built successfully\r
+        if (self.BuildItem.BuildObject in GlobalData.gModuleBuildTracking and\r
+           GlobalData.gModuleBuildTracking[self.BuildItem.BuildObject] != 'FAIL_METAFILE' and\r
+           not BuildTask._ErrorFlag.isSet()\r
+           ):\r
+            GlobalData.gModuleBuildTracking[self.BuildItem.BuildObject] = 'SUCCESS'\r
+\r
         # indicate there's a thread is available for another build task\r
         BuildTask._RunningQueueLock.acquire()\r
         BuildTask._RunningQueue.pop(self.BuildItem)\r
@@ -686,7 +695,7 @@ class Build():
     #   @param  WorkspaceDir        The directory of workspace\r
     #   @param  BuildOptions        Build options passed from command line\r
     #\r
-    def __init__(self, Target, WorkspaceDir, BuildOptions):\r
+    def __init__(self, Target, WorkspaceDir, BuildOptions,log_q):\r
         self.WorkspaceDir   = WorkspaceDir\r
         self.Target         = Target\r
         self.PlatformFile   = BuildOptions.PlatformFile\r
@@ -699,7 +708,7 @@ class Build():
         self.FvList         = BuildOptions.FvImage\r
         self.CapList        = BuildOptions.CapName\r
         self.SilentMode     = BuildOptions.SilentMode\r
-        self.ThreadNumber   = BuildOptions.ThreadNumber\r
+        self.ThreadNumber   = 1\r
         self.SkipAutoGen    = BuildOptions.SkipAutoGen\r
         self.Reparse        = BuildOptions.Reparse\r
         self.SkuId          = BuildOptions.SkuId\r
@@ -708,8 +717,8 @@ class Build():
         self.ConfDirectory = BuildOptions.ConfDirectory\r
         self.SpawnMode      = True\r
         self.BuildReport    = BuildReport(BuildOptions.ReportFile, BuildOptions.ReportType)\r
-        self.TargetTxt      = TargetTxtClassObject()\r
-        self.ToolDef        = ToolDefClassObject()\r
+        self.TargetTxt      = TargetTxt\r
+        self.ToolDef        = ToolDef\r
         self.AutoGenTime    = 0\r
         self.MakeTime       = 0\r
         self.GenFdsTime     = 0\r
@@ -749,24 +758,10 @@ class Build():
             if GlobalData.gBinCacheDest is not None:\r
                 EdkLogger.error("build", OPTION_VALUE_INVALID, ExtraData="Invalid value of option --binary-destination.")\r
 \r
-        if self.ConfDirectory:\r
-            # Get alternate Conf location, if it is absolute, then just use the absolute directory name\r
-            ConfDirectoryPath = os.path.normpath(self.ConfDirectory)\r
-\r
-            if not os.path.isabs(ConfDirectoryPath):\r
-                # Since alternate directory name is not absolute, the alternate directory is located within the WORKSPACE\r
-                # This also handles someone specifying the Conf directory in the workspace. Using --conf=Conf\r
-                ConfDirectoryPath = mws.join(self.WorkspaceDir, ConfDirectoryPath)\r
-        else:\r
-            if "CONF_PATH" in os.environ:\r
-                ConfDirectoryPath = os.path.normcase(os.path.normpath(os.environ["CONF_PATH"]))\r
-            else:\r
-                # Get standard WORKSPACE/Conf use the absolute path to the WORKSPACE/Conf\r
-                ConfDirectoryPath = mws.join(self.WorkspaceDir, 'Conf')\r
-        GlobalData.gConfDirectory = ConfDirectoryPath\r
-        GlobalData.gDatabasePath = os.path.normpath(os.path.join(ConfDirectoryPath, GlobalData.gDatabasePath))\r
-\r
-        self.Db = WorkspaceDatabase()\r
+        GlobalData.gDatabasePath = os.path.normpath(os.path.join(GlobalData.gConfDirectory, GlobalData.gDatabasePath))\r
+        if not os.path.exists(os.path.join(GlobalData.gConfDirectory, '.cache')):\r
+            os.makedirs(os.path.join(GlobalData.gConfDirectory, '.cache'))\r
+        self.Db = BuildDB\r
         self.BuildDatabase = self.Db.BuildObject\r
         self.Platform = None\r
         self.ToolChainFamily = None\r
@@ -807,36 +802,65 @@ class Build():
             EdkLogger.quiet("%-16s = %s" % ("POSTBUILD", self.Postbuild))\r
         if self.Prebuild:\r
             self.LaunchPrebuild()\r
-            self.TargetTxt = TargetTxtClassObject()\r
-            self.ToolDef   = ToolDefClassObject()\r
+            self.TargetTxt = TargetTxt\r
+            self.ToolDef   = ToolDef\r
         if not (self.LaunchPrebuildFlag and os.path.exists(self.PlatformBuildPath)):\r
             self.InitBuild()\r
 \r
+        self.AutoGenMgr = None\r
         EdkLogger.info("")\r
         os.chdir(self.WorkspaceDir)\r
+        GlobalData.gCacheIR = Manager().dict()\r
+        self.log_q = log_q\r
+        GlobalData.file_lock =  mp.Lock()\r
+        GlobalData.cache_lock = mp.Lock()\r
+    def StartAutoGen(self,mqueue, DataPipe,SkipAutoGen,PcdMaList,share_data):\r
+        try:\r
+            if SkipAutoGen:\r
+                return True,0\r
+            feedback_q = mp.Queue()\r
+            error_event = mp.Event()\r
+            FfsCmd = DataPipe.Get("FfsCommand")\r
+            if FfsCmd is None:\r
+                FfsCmd = {}\r
+            GlobalData.FfsCmd = FfsCmd\r
+            GlobalData.libConstPcd = DataPipe.Get("LibConstPcd")\r
+            GlobalData.Refes = DataPipe.Get("REFS")\r
+            auto_workers = [AutoGenWorkerInProcess(mqueue,DataPipe.dump_file,feedback_q,GlobalData.file_lock,GlobalData.cache_lock,share_data,self.log_q,error_event) for _ in range(self.ThreadNumber)]\r
+            self.AutoGenMgr = AutoGenManager(auto_workers,feedback_q,error_event)\r
+            self.AutoGenMgr.start()\r
+            for w in auto_workers:\r
+                w.start()\r
+            if PcdMaList is not None:\r
+                for PcdMa in PcdMaList:\r
+                    if GlobalData.gBinCacheSource and self.Target in [None, "", "all"]:\r
+                        PcdMa.GenModuleFilesHash(share_data)\r
+                        PcdMa.GenPreMakefileHash(share_data)\r
+                        if PcdMa.CanSkipbyPreMakefileCache(share_data):\r
+                           continue\r
+\r
+                    PcdMa.CreateCodeFile(False)\r
+                    PcdMa.CreateMakeFile(False,GenFfsList = DataPipe.Get("FfsCommand").get((PcdMa.MetaFile.File, PcdMa.Arch),[]))\r
+\r
+                    if GlobalData.gBinCacheSource and self.Target in [None, "", "all"]:\r
+                        PcdMa.GenMakeHeaderFilesHash(share_data)\r
+                        PcdMa.GenMakeHash(share_data)\r
+                        if PcdMa.CanSkipbyMakeCache(share_data):\r
+                            continue\r
+\r
+            self.AutoGenMgr.join()\r
+            rt = self.AutoGenMgr.Status\r
+            return rt, 0\r
+        except FatalError as e:\r
+            return False, e.args[0]\r
+        except:\r
+            return False, UNKNOWN_ERROR\r
 \r
     ## Load configuration\r
     #\r
     #   This method will parse target.txt and get the build configurations.\r
     #\r
     def LoadConfiguration(self):\r
-        #\r
-        # Check target.txt and tools_def.txt and Init them\r
-        #\r
-        BuildConfigurationFile = os.path.normpath(os.path.join(GlobalData.gConfDirectory, gBuildConfiguration))\r
-        if os.path.isfile(BuildConfigurationFile) == True:\r
-            StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)\r
-\r
-            ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TOOL_CHAIN_CONF]\r
-            if ToolDefinitionFile == '':\r
-                ToolDefinitionFile = gToolsDefinition\r
-                ToolDefinitionFile = os.path.normpath(mws.join(self.WorkspaceDir, 'Conf', ToolDefinitionFile))\r
-            if os.path.isfile(ToolDefinitionFile) == True:\r
-                StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile)\r
-            else:\r
-                EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)\r
-        else:\r
-            EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)\r
 \r
         # if no ARCH given in command line, get it from target.txt\r
         if not self.ArchList:\r
@@ -878,19 +902,6 @@ class Build():
                 ToolChainFamily.append(ToolDefinition[TAB_TOD_DEFINES_FAMILY][Tool])\r
         self.ToolChainFamily = ToolChainFamily\r
 \r
-        if self.ThreadNumber is None:\r
-            self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]\r
-            if self.ThreadNumber == '':\r
-                self.ThreadNumber = 0\r
-            else:\r
-                self.ThreadNumber = int(self.ThreadNumber, 0)\r
-\r
-        if self.ThreadNumber == 0:\r
-            try:\r
-                self.ThreadNumber = multiprocessing.cpu_count()\r
-            except (ImportError, NotImplementedError):\r
-                self.ThreadNumber = 1\r
-\r
         if not self.PlatformFile:\r
             PlatformFile = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_ACTIVE_PLATFORM]\r
             if not PlatformFile:\r
@@ -908,7 +919,7 @@ class Build():
                                     ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")\r
 \r
             self.PlatformFile = PathClass(NormFile(PlatformFile, self.WorkspaceDir), self.WorkspaceDir)\r
-\r
+        self.ThreadNumber   = ThreadNum()\r
     ## Initialize build configuration\r
     #\r
     #   This method will parse DSC file and merge the configurations from\r
@@ -1154,25 +1165,27 @@ class Build():
     #\r
     #\r
     def invalidateHash(self):\r
-        # GlobalData.gModuleBuildTracking contains only modules that cannot be skipped by hash\r
-        for moduleAutoGenObj in GlobalData.gModuleBuildTracking.keys():\r
-            # False == FAIL : True == Success\r
-            # Skip invalidating for Successful module builds\r
-            if GlobalData.gModuleBuildTracking[moduleAutoGenObj] == True:\r
+        # Only for hashing feature\r
+        if not GlobalData.gUseHashCache:\r
+            return\r
+\r
+        # GlobalData.gModuleBuildTracking contains only modules or libs that cannot be skipped by hash\r
+        for Ma in GlobalData.gModuleBuildTracking:\r
+            # Skip invalidating for Successful Module/Lib builds\r
+            if GlobalData.gModuleBuildTracking[Ma] == 'SUCCESS':\r
                 continue\r
 \r
-            # The module failed to build or failed to start building, from this point on\r
+            # The module failed to build, failed to start building, or failed the header check test from this point on\r
 \r
             # Remove .hash from build\r
-            if GlobalData.gUseHashCache:\r
-                ModuleHashFile = path.join(moduleAutoGenObj.BuildDir, moduleAutoGenObj.Name + ".hash")\r
-                if os.path.exists(ModuleHashFile):\r
-                    os.remove(ModuleHashFile)\r
+            ModuleHashFile = os.path.join(Ma.BuildDir, Ma.Name + ".hash")\r
+            if os.path.exists(ModuleHashFile):\r
+                os.remove(ModuleHashFile)\r
 \r
             # Remove .hash file from cache\r
             if GlobalData.gBinCacheDest:\r
-                FileDir = path.join(GlobalData.gBinCacheDest, moduleAutoGenObj.Arch, moduleAutoGenObj.SourceDir, moduleAutoGenObj.MetaFile.BaseName)\r
-                HashFile = path.join(FileDir, moduleAutoGenObj.Name + '.hash')\r
+                FileDir = os.path.join(GlobalData.gBinCacheDest, Ma.PlatformInfo.OutputDir, Ma.BuildTarget + "_" + Ma.ToolChain, Ma.Arch, Ma.SourceDir, Ma.MetaFile.BaseName)\r
+                HashFile = os.path.join(FileDir, Ma.Name + '.hash')\r
                 if os.path.exists(HashFile):\r
                     os.remove(HashFile)\r
 \r
@@ -1192,30 +1205,34 @@ class Build():
     #   @param  CreateDepModuleMakeFile     Flag used to indicate creating makefile\r
     #                                       for dependent modules/Libraries\r
     #\r
-    def _BuildPa(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True, BuildModule=False, FfsCommand={}):\r
+    def _BuildPa(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True, BuildModule=False, FfsCommand=None, PcdMaList=None):\r
         if AutoGenObject is None:\r
             return False\r
-\r
+        if FfsCommand is None:\r
+            FfsCommand = {}\r
         # skip file generation for cleanxxx targets, run and fds target\r
         if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
             # for target which must generate AutoGen code and makefile\r
-            if not self.SkipAutoGen or Target == 'genc':\r
-                self.Progress.Start("Generating code")\r
-                AutoGenObject.CreateCodeFile(CreateDepsCodeFile)\r
-                self.Progress.Stop("done!")\r
-            if Target == "genc":\r
-                return True\r
-\r
-            if not self.SkipAutoGen or Target == 'genmake':\r
-                self.Progress.Start("Generating makefile")\r
-                AutoGenObject.CreateMakeFile(CreateDepsMakeFile, FfsCommand)\r
-                self.Progress.Stop("done!")\r
-            if Target == "genmake":\r
-                return True\r
-        else:\r
-            # always recreate top/platform makefile when clean, just in case of inconsistency\r
+            mqueue = mp.Queue()\r
+            for m in AutoGenObject.GetAllModuleInfo:\r
+                mqueue.put(m)\r
+\r
+            AutoGenObject.DataPipe.DataContainer = {"CommandTarget": self.Target}\r
+            self.Progress.Start("Generating makefile and code")\r
+            data_pipe_file = os.path.join(AutoGenObject.BuildDir, "GlobalVar_%s_%s.bin" % (str(AutoGenObject.Guid),AutoGenObject.Arch))\r
+            AutoGenObject.DataPipe.dump(data_pipe_file)\r
+            autogen_rt,errorcode = self.StartAutoGen(mqueue, AutoGenObject.DataPipe, self.SkipAutoGen, PcdMaList, GlobalData.gCacheIR)\r
+            self.Progress.Stop("done!")\r
+            if not autogen_rt:\r
+                self.AutoGenMgr.TerminateWorkers()\r
+                self.AutoGenMgr.join(0.1)\r
+                raise FatalError(errorcode)\r
             AutoGenObject.CreateCodeFile(False)\r
             AutoGenObject.CreateMakeFile(False)\r
+        else:\r
+            # always recreate top/platform makefile when clean, just in case of inconsistency\r
+            AutoGenObject.CreateCodeFile(True)\r
+            AutoGenObject.CreateMakeFile(True)\r
 \r
         if EdkLogger.GetLevel() == EdkLogger.QUIET:\r
             EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))\r
@@ -1232,10 +1249,6 @@ class Build():
 \r
         # run\r
         if Target == 'run':\r
-            RunDir = os.path.normpath(os.path.join(AutoGenObject.BuildDir, GlobalData.gGlobalDefines['ARCH']))\r
-            Command = '.\SecMain'\r
-            os.chdir(RunDir)\r
-            LaunchCommand(Command, RunDir)\r
             return True\r
 \r
         # build modules\r
@@ -1243,6 +1256,9 @@ class Build():
             BuildCommand = BuildCommand + [Target]\r
             LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)\r
             self.CreateAsBuiltInf()\r
+            if GlobalData.gBinCacheDest:\r
+                self.UpdateBuildCache()\r
+            self.BuildModules = []\r
             return True\r
 \r
         # build library\r
@@ -1261,6 +1277,9 @@ class Build():
                 NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Mod, makefile)), 'pbuild']\r
                 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
             self.CreateAsBuiltInf()\r
+            if GlobalData.gBinCacheDest:\r
+                self.UpdateBuildCache()\r
+            self.BuildModules = []\r
             return True\r
 \r
         # cleanlib\r
@@ -1334,8 +1353,8 @@ class Build():
                 return True\r
         else:\r
             # always recreate top/platform makefile when clean, just in case of inconsistency\r
-            AutoGenObject.CreateCodeFile(False)\r
-            AutoGenObject.CreateMakeFile(False)\r
+            AutoGenObject.CreateCodeFile(True)\r
+            AutoGenObject.CreateMakeFile(True)\r
 \r
         if EdkLogger.GetLevel() == EdkLogger.QUIET:\r
             EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))\r
@@ -1354,6 +1373,9 @@ class Build():
                 BuildCommand = BuildCommand + [Target]\r
             AutoGenObject.BuildTime = LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)\r
             self.CreateAsBuiltInf()\r
+            if GlobalData.gBinCacheDest:\r
+                self.UpdateBuildCache()\r
+            self.BuildModules = []\r
             return True\r
 \r
         # genfds\r
@@ -1364,10 +1386,6 @@ class Build():
 \r
         # run\r
         if Target == 'run':\r
-            RunDir = os.path.normpath(os.path.join(AutoGenObject.BuildDir, GlobalData.gGlobalDefines['ARCH']))\r
-            Command = '.\SecMain'\r
-            os.chdir(RunDir)\r
-            LaunchCommand(Command, RunDir)\r
             return True\r
 \r
         # build library\r
@@ -1487,7 +1505,7 @@ class Build():
         if self.Fdf:\r
             # First get the XIP base address for FV map file.\r
             GuidPattern = re.compile("[-a-fA-F0-9]+")\r
-            GuidName = re.compile("\(GUID=[-a-fA-F0-9]+")\r
+            GuidName = re.compile(r"\(GUID=[-a-fA-F0-9]+")\r
             for FvName in Wa.FdfProfile.FvDict:\r
                 FvMapBuffer = os.path.join(Wa.FvDir, FvName + '.Fv.map')\r
                 if not os.path.exists(FvMapBuffer):\r
@@ -1704,15 +1722,22 @@ class Build():
                     CmdListDict = self._GenFfsCmd(Wa.ArchList)\r
 \r
                 for Arch in Wa.ArchList:\r
+                    PcdMaList    = []\r
                     GlobalData.gGlobalDefines['ARCH'] = Arch\r
                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
                     for Module in Pa.Platform.Modules:\r
                         # Get ModuleAutoGen object to generate C code file and makefile\r
-                        Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
+                        Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile,Pa.DataPipe)\r
                         if Ma is None:\r
                             continue\r
+                        if Ma.PcdIsDriver:\r
+                            Ma.PlatformInfo = Pa\r
+                            Ma.Workspace = Wa\r
+                            PcdMaList.append(Ma)\r
                         self.BuildModules.append(Ma)\r
-                    self._BuildPa(self.Target, Pa, FfsCommand=CmdListDict)\r
+                    Pa.DataPipe.DataContainer = {"FfsCommand":CmdListDict}\r
+                    Pa.DataPipe.DataContainer = {"Workspace_timestamp": Wa._SrcTimeStamp}\r
+                    self._BuildPa(self.Target, Pa, FfsCommand=CmdListDict,PcdMaList=PcdMaList)\r
 \r
                 # Create MAP file when Load Fix Address is enabled.\r
                 if self.Target in ["", "all", "fds"]:\r
@@ -1795,6 +1820,16 @@ class Build():
                 CmdListDict = None\r
                 if GlobalData.gEnableGenfdsMultiThread and self.Fdf:\r
                     CmdListDict = self._GenFfsCmd(Wa.ArchList)\r
+\r
+                # Add Platform and Package level hash in share_data for module hash calculation later\r
+                if GlobalData.gBinCacheSource or GlobalData.gBinCacheDest:\r
+                    GlobalData.gCacheIR[('PlatformHash')] = GlobalData.gPlatformHash\r
+                    for PkgName in GlobalData.gPackageHash.keys():\r
+                        GlobalData.gCacheIR[(PkgName, 'PackageHash')] = GlobalData.gPackageHash[PkgName]\r
+                GlobalData.file_lock = mp.Lock()\r
+                GlobalData.cache_lock = mp.Lock()\r
+                GlobalData.FfsCmd = CmdListDict\r
+\r
                 self.Progress.Stop("done!")\r
                 MaList = []\r
                 ExitFlag = threading.Event()\r
@@ -1804,15 +1839,23 @@ class Build():
                     AutoGenStart = time.time()\r
                     GlobalData.gGlobalDefines['ARCH'] = Arch\r
                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
+                    GlobalData.libConstPcd = Pa.DataPipe.Get("LibConstPcd")\r
+                    GlobalData.Refes = Pa.DataPipe.Get("REFS")\r
                     for Module in Pa.Platform.Modules:\r
                         if self.ModuleFile.Dir == Module.Dir and self.ModuleFile.Name == Module.Name:\r
-                            Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
+                            Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile,Pa.DataPipe)\r
                             if Ma is None:\r
                                 continue\r
                             MaList.append(Ma)\r
-                            if Ma.CanSkipbyHash():\r
-                                self.HashSkipModules.append(Ma)\r
-                                continue\r
+\r
+                            if GlobalData.gBinCacheSource and self.Target in [None, "", "all"]:\r
+                                Ma.GenModuleFilesHash(GlobalData.gCacheIR)\r
+                                Ma.GenPreMakefileHash(GlobalData.gCacheIR)\r
+                                if Ma.CanSkipbyPreMakefileCache(GlobalData.gCacheIR):\r
+                                   self.HashSkipModules.append(Ma)\r
+                                   EdkLogger.quiet("cache hit: %s[%s]" % (Ma.MetaFile.Path, Ma.Arch))\r
+                                   continue\r
+\r
                             # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
                             if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
                                 # for target which must generate AutoGen code and makefile\r
@@ -1832,15 +1875,26 @@ class Build():
                                     self.Progress.Stop("done!")\r
                                 if self.Target == "genmake":\r
                                     return True\r
+\r
+                                if GlobalData.gBinCacheSource and self.Target in [None, "", "all"]:\r
+                                    Ma.GenMakeHeaderFilesHash(GlobalData.gCacheIR)\r
+                                    Ma.GenMakeHash(GlobalData.gCacheIR)\r
+                                    if Ma.CanSkipbyMakeCache(GlobalData.gCacheIR):\r
+                                        self.HashSkipModules.append(Ma)\r
+                                        EdkLogger.quiet("cache hit: %s[%s]" % (Ma.MetaFile.Path, Ma.Arch))\r
+                                        continue\r
+                                    else:\r
+                                        EdkLogger.quiet("cache miss: %s[%s]" % (Ma.MetaFile.Path, Ma.Arch))\r
+                                        Ma.PrintFirstMakeCacheMissFile(GlobalData.gCacheIR)\r
+\r
                             self.BuildModules.append(Ma)\r
-                            # Initialize all modules in tracking to False (FAIL)\r
-                            if Ma not in GlobalData.gModuleBuildTracking:\r
-                                GlobalData.gModuleBuildTracking[Ma] = False\r
+                            # Initialize all modules in tracking to 'FAIL'\r
+                            GlobalData.gModuleBuildTracking[Ma] = 'FAIL'\r
                     self.AutoGenTime += int(round((time.time() - AutoGenStart)))\r
                     MakeStart = time.time()\r
                     for Ma in self.BuildModules:\r
                         if not Ma.IsBinaryModule:\r
-                            Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
+                            Bt = BuildTask.New(ModuleMakeUnit(Ma, Pa.BuildCommand,self.Target))\r
                         # Break build if any build thread has error\r
                         if BuildTask.HasError():\r
                             # we need a full version of makefile for platform\r
@@ -1864,6 +1918,9 @@ class Build():
                 ExitFlag.set()\r
                 BuildTask.WaitForComplete()\r
                 self.CreateAsBuiltInf()\r
+                if GlobalData.gBinCacheDest:\r
+                    self.UpdateBuildCache()\r
+                self.BuildModules = []\r
                 self.MakeTime += int(round((time.time() - MakeContiue)))\r
                 if BuildTask.HasError():\r
                     self.invalidateHash()\r
@@ -1919,6 +1976,7 @@ class Build():
                     # Save MAP buffer into MAP file.\r
                     #\r
                     self._SaveMapFile (MapBuffer, Wa)\r
+        self.invalidateHash()\r
 \r
     def _GenFfsCmd(self,ArchList):\r
         # convert dictionary of Cmd:(Inf,Arch)\r
@@ -1966,15 +2024,24 @@ class Build():
                 Wa.CreateMakeFile(False)\r
 \r
                 # Add ffs build to makefile\r
-                CmdListDict = None\r
+                CmdListDict = {}\r
                 if GlobalData.gEnableGenfdsMultiThread and self.Fdf:\r
                     CmdListDict = self._GenFfsCmd(Wa.ArchList)\r
 \r
+                # Add Platform and Package level hash in share_data for module hash calculation later\r
+                if GlobalData.gBinCacheSource or GlobalData.gBinCacheDest:\r
+                    GlobalData.gCacheIR[('PlatformHash')] = GlobalData.gPlatformHash\r
+                    for PkgName in GlobalData.gPackageHash.keys():\r
+                        GlobalData.gCacheIR[(PkgName, 'PackageHash')] = GlobalData.gPackageHash[PkgName]\r
+\r
                 # multi-thread exit flag\r
                 ExitFlag = threading.Event()\r
                 ExitFlag.clear()\r
                 self.AutoGenTime += int(round((time.time() - WorkspaceAutoGenTime)))\r
+                self.BuildModules = []\r
+                TotalModules = []\r
                 for Arch in Wa.ArchList:\r
+                    PcdMaList    = []\r
                     AutoGenStart = time.time()\r
                     GlobalData.gGlobalDefines['ARCH'] = Arch\r
                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
@@ -1990,43 +2057,72 @@ class Build():
                             if Inf in Pa.Platform.Modules:\r
                                 continue\r
                             ModuleList.append(Inf)\r
+                    Pa.DataPipe.DataContainer = {"FfsCommand":CmdListDict}\r
+                    Pa.DataPipe.DataContainer = {"Workspace_timestamp": Wa._SrcTimeStamp}\r
+                    Pa.DataPipe.DataContainer = {"CommandTarget": self.Target}\r
                     for Module in ModuleList:\r
                         # Get ModuleAutoGen object to generate C code file and makefile\r
-                        Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
+                        Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile,Pa.DataPipe)\r
 \r
                         if Ma is None:\r
                             continue\r
-                        if Ma.CanSkipbyHash():\r
-                            self.HashSkipModules.append(Ma)\r
-                            continue\r
+                        if Ma.PcdIsDriver:\r
+                            Ma.PlatformInfo = Pa\r
+                            Ma.Workspace = Wa\r
+                            PcdMaList.append(Ma)\r
+                        TotalModules.append(Ma)\r
+                        # Initialize all modules in tracking to 'FAIL'\r
+                        GlobalData.gModuleBuildTracking[Ma] = 'FAIL'\r
+\r
+                    mqueue = mp.Queue()\r
+                    for m in Pa.GetAllModuleInfo:\r
+                        mqueue.put(m)\r
+                    data_pipe_file = os.path.join(Pa.BuildDir, "GlobalVar_%s_%s.bin" % (str(Pa.Guid),Pa.Arch))\r
+                    Pa.DataPipe.dump(data_pipe_file)\r
+                    autogen_rt, errorcode = self.StartAutoGen(mqueue, Pa.DataPipe, self.SkipAutoGen, PcdMaList,  GlobalData.gCacheIR)\r
+\r
+                    # Skip cache hit modules\r
+                    if GlobalData.gBinCacheSource:\r
+                        for Ma in TotalModules:\r
+                            if (Ma.MetaFile.Path, Ma.Arch) in GlobalData.gCacheIR and \\r
+                                GlobalData.gCacheIR[(Ma.MetaFile.Path, Ma.Arch)].PreMakeCacheHit:\r
+                                    self.HashSkipModules.append(Ma)\r
+                                    continue\r
+                            if (Ma.MetaFile.Path, Ma.Arch) in GlobalData.gCacheIR and \\r
+                                GlobalData.gCacheIR[(Ma.MetaFile.Path, Ma.Arch)].MakeCacheHit:\r
+                                    self.HashSkipModules.append(Ma)\r
+                                    continue\r
+                            self.BuildModules.append(Ma)\r
+                    else:\r
+                        self.BuildModules.extend(TotalModules)\r
+\r
+                    if not autogen_rt:\r
+                        self.AutoGenMgr.TerminateWorkers()\r
+                        self.AutoGenMgr.join(0.1)\r
+                        raise FatalError(errorcode)\r
+                self.AutoGenTime += int(round((time.time() - AutoGenStart)))\r
+                self.Progress.Stop("done!")\r
 \r
-                        # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
-                        if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
-                            # for target which must generate AutoGen code and makefile\r
-                            if not self.SkipAutoGen or self.Target == 'genc':\r
-                                Ma.CreateCodeFile(True)\r
-                            if self.Target == "genc":\r
-                                continue\r
+                if GlobalData.gBinCacheSource:\r
+                    EdkLogger.quiet("Total cache hit driver num: %s, cache miss driver num: %s" % (len(set(self.HashSkipModules)), len(set(self.BuildModules))))\r
+                    CacheHitMa = set()\r
+                    CacheNotHitMa = set()\r
+                    for IR in GlobalData.gCacheIR.keys():\r
+                        if 'PlatformHash' in IR or 'PackageHash' in IR:\r
+                            continue\r
+                        if GlobalData.gCacheIR[IR].PreMakeCacheHit or GlobalData.gCacheIR[IR].MakeCacheHit:\r
+                            CacheHitMa.add(IR)\r
+                        else:\r
+                            # There might be binary module or module which has .inc files, not count for cache miss\r
+                            CacheNotHitMa.add(IR)\r
+                    EdkLogger.quiet("Total module num: %s, cache hit module num: %s" % (len(CacheHitMa)+len(CacheNotHitMa), len(CacheHitMa)))\r
 \r
-                            if not self.SkipAutoGen or self.Target == 'genmake':\r
-                                if CmdListDict and self.Fdf and (Module.File, Arch) in CmdListDict:\r
-                                    Ma.CreateMakeFile(True, CmdListDict[Module.File, Arch])\r
-                                    del CmdListDict[Module.File, Arch]\r
-                                else:\r
-                                    Ma.CreateMakeFile(True)\r
-                            if self.Target == "genmake":\r
-                                continue\r
-                        self.BuildModules.append(Ma)\r
-                        # Initialize all modules in tracking to False (FAIL)\r
-                        if Ma not in GlobalData.gModuleBuildTracking:\r
-                            GlobalData.gModuleBuildTracking[Ma] = False\r
-                    self.Progress.Stop("done!")\r
-                    self.AutoGenTime += int(round((time.time() - AutoGenStart)))\r
+                for Arch in Wa.ArchList:\r
                     MakeStart = time.time()\r
-                    for Ma in self.BuildModules:\r
+                    for Ma in set(self.BuildModules):\r
                         # Generate build task for the module\r
                         if not Ma.IsBinaryModule:\r
-                            Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
+                            Bt = BuildTask.New(ModuleMakeUnit(Ma, Pa.BuildCommand,self.Target))\r
                         # Break build if any build thread has error\r
                         if BuildTask.HasError():\r
                             # we need a full version of makefile for platform\r
@@ -2056,6 +2152,9 @@ class Build():
                 ExitFlag.set()\r
                 BuildTask.WaitForComplete()\r
                 self.CreateAsBuiltInf()\r
+                if GlobalData.gBinCacheDest:\r
+                    self.UpdateBuildCache()\r
+                self.BuildModules = []\r
                 self.MakeTime += int(round((time.time() - MakeContiue)))\r
                 #\r
                 # Check for build error, and raise exception if one\r
@@ -2107,6 +2206,7 @@ class Build():
                     # Save MAP buffer into MAP file.\r
                     #\r
                     self._SaveMapFile(MapBuffer, Wa)\r
+        self.invalidateHash()\r
 \r
     ## Generate GuidedSectionTools.txt in the FV directories.\r
     #\r
@@ -2194,22 +2294,25 @@ class Build():
             RemoveDirectory(os.path.dirname(GlobalData.gDatabasePath), True)\r
 \r
     def CreateAsBuiltInf(self):\r
+        for Module in self.BuildModules:\r
+            Module.CreateAsBuiltInf()\r
+\r
+    def UpdateBuildCache(self):\r
         all_lib_set = set()\r
         all_mod_set = set()\r
         for Module in self.BuildModules:\r
-            Module.CreateAsBuiltInf()\r
+            Module.CopyModuleToCache()\r
             all_mod_set.add(Module)\r
         for Module in self.HashSkipModules:\r
-            Module.CreateAsBuiltInf(True)\r
+            Module.CopyModuleToCache()\r
             all_mod_set.add(Module)\r
         for Module in all_mod_set:\r
             for lib in Module.LibraryAutoGenList:\r
                 all_lib_set.add(lib)\r
         for lib in all_lib_set:\r
-            lib.CreateAsBuiltInf(True)\r
+            lib.CopyModuleToCache()\r
         all_lib_set.clear()\r
         all_mod_set.clear()\r
-        self.BuildModules = []\r
         self.HashSkipModules = []\r
     ## Do some clean-up works when error occurred\r
     def Relinquish(self):\r
@@ -2236,13 +2339,7 @@ def ParseDefines(DefineList=[]):
                 DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()\r
     return DefineDict\r
 \r
-gParamCheck = []\r
-def SingleCheckCallback(option, opt_str, value, parser):\r
-    if option not in gParamCheck:\r
-        setattr(parser.values, option.dest, value)\r
-        gParamCheck.append(option)\r
-    else:\r
-        parser.error("Option %s only allows one instance in command line!" % option)\r
+\r
 \r
 def LogBuildTime(Time):\r
     if Time:\r
@@ -2255,80 +2352,21 @@ def LogBuildTime(Time):
         return TimeDurStr\r
     else:\r
         return None\r
+def ThreadNum():\r
+    ThreadNumber = BuildOption.ThreadNumber\r
+    if ThreadNumber is None:\r
+        ThreadNumber = TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]\r
+        if ThreadNumber == '':\r
+            ThreadNumber = 0\r
+        else:\r
+            ThreadNumber = int(ThreadNumber, 0)\r
 \r
-## Parse command line options\r
-#\r
-# Using standard Python module optparse to parse command line option of this tool.\r
-#\r
-#   @retval Opt   A optparse.Values object containing the parsed options\r
-#   @retval Args  Target of build command\r
-#\r
-def MyOptionParser():\r
-    Parser = OptionParser(description=__copyright__, version=__version__, prog="build.exe", usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")\r
-    Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32', 'X64', 'EBC', 'ARM', 'AARCH64'], dest="TargetArch",\r
-        help="ARCHS is one of list: IA32, X64, ARM, AARCH64 or EBC, which overrides target.txt's TARGET_ARCH definition. To specify more archs, please repeat this option.")\r
-    Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback,\r
-        help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")\r
-    Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback,\r
-        help="Build the module specified by the INF file name argument.")\r
-    Parser.add_option("-b", "--buildtarget", type="string", dest="BuildTarget", help="Using the TARGET to build the platform, overriding target.txt's TARGET definition.",\r
-                      action="append")\r
-    Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",\r
-        help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")\r
-    Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback,\r
-        help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")\r
-\r
-    Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback,\r
-        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 "\\r
-             "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.")\r
-\r
-    Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback,\r
-        help="The name of the FDF file to use, which overrides the setting in the DSC file.")\r
-    Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],\r
-        help="The name of FD to be generated. The name must be from [FD] section in FDF file.")\r
-    Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],\r
-        help="The name of FV to be generated. The name must be from [FV] section in FDF file.")\r
-    Parser.add_option("-C", "--capsule-image", action="append", type="string", dest="CapName", default=[],\r
-        help="The name of Capsule to be generated. The name must be from [Capsule] section in FDF file.")\r
-    Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.")\r
-    Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.")\r
-\r
-    Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", default=False, help="Don't check case of file name.")\r
-\r
-    Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")\r
-    Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.")\r
-\r
-    Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode",\r
-        help="Make use of silent mode of (n)make.")\r
-    Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
-    Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
-                                                                               "including library instances selected, final dependency expression, "\\r
-                                                                               "and warning messages, etc.")\r
-    Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
-    Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")\r
-\r
-    Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.")\r
-    Parser.add_option("-Y", "--report-type", action="append", type="choice", choices=['PCD', 'LIBRARY', 'FLASH', 'DEPEX', 'BUILD_FLAGS', 'FIXED_ADDRESS', 'HASH', 'EXECUTION_ORDER'], dest="ReportType", default=[],\r
-        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].  "\\r
-             "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]")\r
-    Parser.add_option("-F", "--flag", action="store", type="string", dest="Flag",\r
-        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. "\\r
-             "This option can also be specified by setting *_*_*_BUILD_FLAGS in [BuildOptions] section of platform DSC. If they are both specified, this value "\\r
-             "will override the setting in [BuildOptions] section of platform DSC.")\r
-    Parser.add_option("-N", "--no-cache", action="store_true", dest="DisableCache", default=False, help="Disable build cache mechanism")\r
-    Parser.add_option("--conf", action="store", type="string", dest="ConfDirectory", help="Specify the customized Conf directory.")\r
-    Parser.add_option("--check-usage", action="store_true", dest="CheckUsage", default=False, help="Check usage content of entries listed in INF file.")\r
-    Parser.add_option("--ignore-sources", action="store_true", dest="IgnoreSources", default=False, help="Focus to a binary build and ignore all source files")\r
-    Parser.add_option("--pcd", action="append", dest="OptionPcd", help="Set PCD value by command line. Format: \"PcdName=Value\" ")\r
-    Parser.add_option("-l", "--cmd-len", action="store", type="int", dest="CommandLength", help="Specify the maximum line length of build command. Default is 4096.")\r
-    Parser.add_option("--hash", action="store_true", dest="UseHashCache", default=False, help="Enable hash-based caching during build process.")\r
-    Parser.add_option("--binary-destination", action="store", type="string", dest="BinCacheDest", help="Generate a cache of binary files in the specified directory.")\r
-    Parser.add_option("--binary-source", action="store", type="string", dest="BinCacheSource", help="Consume a cache of binary files from the specified directory.")\r
-    Parser.add_option("--genfds-multi-thread", action="store_true", dest="GenfdsMultiThread", default=False, help="Enable GenFds multi thread to generate ffs file.")\r
-    Parser.add_option("--disable-include-path-check", action="store_true", dest="DisableIncludePathCheck", default=False, help="Disable the include path check for outside of package.")\r
-    (Opt, Args) = Parser.parse_args()\r
-    return (Opt, Args)\r
-\r
+    if ThreadNumber == 0:\r
+        try:\r
+            ThreadNumber = multiprocessing.cpu_count()\r
+        except (ImportError, NotImplementedError):\r
+            ThreadNumber = 1\r
+    return ThreadNumber\r
 ## Tool entrance method\r
 #\r
 # This method mainly dispatch specific methods per the command line options.\r
@@ -2338,34 +2376,42 @@ def MyOptionParser():
 #   @retval 0     Tool was successful\r
 #   @retval 1     Tool failed\r
 #\r
+LogQMaxSize = ThreadNum() * 10\r
 def Main():\r
     StartTime = time.time()\r
 \r
+    #\r
+    # Create a log Queue\r
+    #\r
+    LogQ = mp.Queue(LogQMaxSize)\r
     # Initialize log system\r
-    EdkLogger.Initialize()\r
+    EdkLogger.LogClientInitialize(LogQ)\r
     GlobalData.gCommand = sys.argv[1:]\r
     #\r
     # Parse the options and args\r
     #\r
-    (Option, Target) = MyOptionParser()\r
+    Option, Target = BuildOption, BuildTarget\r
     GlobalData.gOptions = Option\r
     GlobalData.gCaseInsensitive = Option.CaseInsensitive\r
 \r
     # Set log level\r
+    LogLevel = EdkLogger.INFO\r
     if Option.verbose is not None:\r
         EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
+        LogLevel = EdkLogger.VERBOSE\r
     elif Option.quiet is not None:\r
         EdkLogger.SetLevel(EdkLogger.QUIET)\r
+        LogLevel = EdkLogger.QUIET\r
     elif Option.debug is not None:\r
         EdkLogger.SetLevel(Option.debug + 1)\r
+        LogLevel = Option.debug + 1\r
     else:\r
         EdkLogger.SetLevel(EdkLogger.INFO)\r
 \r
-    if Option.LogFile is not None:\r
-        EdkLogger.SetLogFile(Option.LogFile)\r
-\r
     if Option.WarningAsError == True:\r
         EdkLogger.SetWarningAsError()\r
+    Log_Agent = LogAgent(LogQ,LogLevel,Option.LogFile)\r
+    Log_Agent.start()\r
 \r
     if platform.platform().find("Windows") >= 0:\r
         GlobalData.gIsWindows = True\r
@@ -2439,7 +2485,7 @@ def Main():
         if Option.Flag is not None and Option.Flag not in ['-c', '-s']:\r
             EdkLogger.error("build", OPTION_VALUE_INVALID, "UNI flag must be one of -c or -s")\r
 \r
-        MyBuild = Build(Target, Workspace, Option)\r
+        MyBuild = Build(Target, Workspace, Option,LogQ)\r
         GlobalData.gCommandLineDefines['ARCH'] = ' '.join(MyBuild.ArchList)\r
         if not (MyBuild.LaunchPrebuildFlag and os.path.exists(MyBuild.PlatformBuildPath)):\r
             MyBuild.Launch()\r
@@ -2466,6 +2512,10 @@ def Main():
             EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError=False)\r
         ReturnCode = FORMAT_INVALID\r
     except KeyboardInterrupt:\r
+        if MyBuild is not None:\r
+\r
+            # for multi-thread build exits safely\r
+            MyBuild.Relinquish()\r
         ReturnCode = ABORT_ERROR\r
         if Option is not None and Option.debug is not None:\r
             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
@@ -2485,7 +2535,7 @@ def Main():
                     "\nbuild",\r
                     CODE_ERROR,\r
                     "Unknown fatal error when processing [%s]" % MetaFile,\r
-                    ExtraData="\n(Please send email to edk2-devel@lists.01.org for help, attaching following call stack trace!)\n",\r
+                    ExtraData="\n(Please send email to %s for help, attaching following call stack trace!)\n" % MSG_EDKII_MAIL_ADDR,\r
                     RaiseError=False\r
                     )\r
         EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
@@ -2519,9 +2569,15 @@ def Main():
     EdkLogger.quiet("\n- %s -" % Conclusion)\r
     EdkLogger.quiet(time.strftime("Build end time: %H:%M:%S, %b.%d %Y", time.localtime()))\r
     EdkLogger.quiet("Build total time: %s\n" % BuildDurationStr)\r
+    Log_Agent.kill()\r
+    Log_Agent.join()\r
     return ReturnCode\r
 \r
 if __name__ == '__main__':\r
+    try:\r
+        mp.set_start_method('spawn')\r
+    except:\r
+        pass\r
     r = Main()\r
     ## 0-127 is a safe return range, and 1 is a standard default error\r
     if r < 0 or r > 127: r = 1\r