from Common.MultipleWorkspace import MultipleWorkspace as mws\r
import InfSectionParser\r
import datetime\r
+import hashlib\r
\r
## Regular expression for splitting Dependency Expression string into tokens\r
gDepexTokenPattern = re.compile("(\(|\)|\w+| \S+\.inf)")\r
self.FvTargetList = Fvs\r
self.CapTargetList = Caps\r
self.AutoGenObjectList = []\r
+ self._BuildDir = None\r
+ self._FvDir = None\r
+ self._MakeFileDir = None\r
+ self._BuildCommand = None\r
\r
# there's many relative directory operations, so ...\r
os.chdir(self.WorkspaceDir)\r
Pa.CollectFixedAtBuildPcds()\r
self.AutoGenObjectList.append(Pa)\r
\r
+ #\r
+ # Generate Package level hash value\r
+ #\r
+ GlobalData.gPackageHash[Arch] = {}\r
+ if GlobalData.gUseHashCache:\r
+ for Pkg in Pkgs:\r
+ self._GenPkgLevelHash(Pkg)\r
+\r
#\r
# Check PCDs token value conflict in each DEC file.\r
#\r
# if self.FdfFile:\r
# self._CheckDuplicateInFV(Fdf)\r
\r
- self._BuildDir = None\r
- self._FvDir = None\r
- self._MakeFileDir = None\r
- self._BuildCommand = None\r
-\r
#\r
# Create BuildOptions Macro & PCD metafile, also add the Active Platform and FDF file.\r
#\r
if self.FdfFile:\r
content += 'Flash Image Definition: '\r
content += str(self.FdfFile)\r
+ content += os.linesep\r
+ if GlobalData.gBinCacheDest:\r
+ content += 'Cache of .efi location: '\r
+ content += str(GlobalData.gBinCacheDest)\r
SaveFileOnChange(os.path.join(self.BuildDir, 'BuildOptions'), content, False)\r
\r
#\r
SrcTimeStamp = os.stat(f)[8]\r
self._SrcTimeStamp = SrcTimeStamp\r
\r
+ if GlobalData.gUseHashCache:\r
+ m = hashlib.md5()\r
+ for files in AllWorkSpaceMetaFiles:\r
+ if files.endswith('.dec'):\r
+ continue\r
+ f = open(files, 'r')\r
+ Content = f.read()\r
+ f.close()\r
+ m.update(Content)\r
+ SaveFileOnChange(os.path.join(self.BuildDir, 'AutoGen.hash'), m.hexdigest(), True)\r
+ GlobalData.gPlatformHash = m.hexdigest()\r
+\r
#\r
# Write metafile list to build directory\r
#\r
print >> file, f\r
return True\r
\r
+ def _GenPkgLevelHash(self, Pkg):\r
+ PkgDir = os.path.join(self.BuildDir, Pkg.Arch, Pkg.PackageName)\r
+ CreateDirectory(PkgDir)\r
+ HashFile = os.path.join(PkgDir, Pkg.PackageName + '.hash')\r
+ m = hashlib.md5()\r
+ # Get .dec file's hash value\r
+ f = open(Pkg.MetaFile.Path, 'r')\r
+ Content = f.read()\r
+ f.close()\r
+ m.update(Content)\r
+ # Get include files hash value\r
+ if Pkg.Includes:\r
+ for inc in Pkg.Includes:\r
+ for Root, Dirs, Files in os.walk(str(inc)):\r
+ for File in Files:\r
+ File_Path = os.path.join(Root, File)\r
+ f = open(File_Path, 'r')\r
+ Content = f.read()\r
+ f.close()\r
+ m.update(Content)\r
+ SaveFileOnChange(HashFile, m.hexdigest(), True)\r
+ if Pkg.PackageName not in GlobalData.gPackageHash[Pkg.Arch]:\r
+ GlobalData.gPackageHash[Pkg.Arch][Pkg.PackageName] = m.hexdigest()\r
\r
def _GetMetaFiles(self, Target, Toolchain, Arch):\r
AllWorkSpaceMetaFiles = set()\r
\r
## Return the directory to store all intermediate and final files built\r
def _GetBuildDir(self):\r
- return self.AutoGenObjectList[0].BuildDir\r
+ if self._BuildDir == None:\r
+ return self.AutoGenObjectList[0].BuildDir\r
\r
## Return the build output directory platform specifies\r
def _GetOutputDir(self):\r
AsBuiltInfDict['module_pi_specification_version'] += [self.Specification['PI_SPECIFICATION_VERSION']]\r
\r
OutputDir = self.OutputDir.replace('\\', '/').strip('/')\r
-\r
+ self.OutputFile = []\r
for Item in self.CodaTargetList:\r
File = Item.Target.Path.replace('\\', '/').strip('/').replace(OutputDir, '').strip('/')\r
+ if File not in self.OutputFile:\r
+ self.OutputFile.append(File)\r
if Item.Target.Ext.lower() == '.aml':\r
AsBuiltInfDict['binary_item'] += ['ASL|' + File]\r
elif Item.Target.Ext.lower() == '.acpi':\r
else:\r
AsBuiltInfDict['binary_item'] += ['BIN|' + File]\r
if self.DepexGenerated:\r
+ if self.Name + '.depex' not in self.OutputFile:\r
+ self.OutputFile.append(self.Name + '.depex')\r
if self.ModuleType in ['PEIM']:\r
AsBuiltInfDict['binary_item'] += ['PEI_DEPEX|' + self.Name + '.depex']\r
if self.ModuleType in ['DXE_DRIVER', 'DXE_RUNTIME_DRIVER', 'DXE_SAL_DRIVER', 'UEFI_DRIVER']:\r
Bin = self._GenOffsetBin()\r
if Bin:\r
AsBuiltInfDict['binary_item'] += ['BIN|%s' % Bin]\r
+ if Bin not in self.OutputFile:\r
+ self.OutputFile.append(Bin)\r
\r
for Root, Dirs, Files in os.walk(OutputDir):\r
for File in Files:\r
if File.lower().endswith('.pdb'):\r
AsBuiltInfDict['binary_item'] += ['DISPOSABLE|' + File]\r
+ if File not in self.OutputFile:\r
+ self.OutputFile.append(File)\r
HeaderComments = self.Module.HeaderComments\r
StartPos = 0\r
for Index in range(len(HeaderComments)):\r
SaveFileOnChange(os.path.join(self.OutputDir, self.Name + '.inf'), str(AsBuiltInf), False)\r
\r
self.IsAsBuiltInfCreated = True\r
- \r
+ if GlobalData.gBinCacheDest:\r
+ self.CopyModuleToCache()\r
+\r
+ def CopyModuleToCache(self):\r
+ FileDir = path.join(GlobalData.gBinCacheDest, self.Arch, self.SourceDir, self.MetaFile.BaseName)\r
+ CreateDirectory (FileDir)\r
+ HashFile = path.join(self.BuildDir, self.Name + '.hash')\r
+ ModuleFile = path.join(self.OutputDir, self.Name + '.inf')\r
+ if os.path.exists(HashFile):\r
+ shutil.copy2(HashFile, FileDir)\r
+ if os.path.exists(ModuleFile):\r
+ shutil.copy2(ModuleFile, FileDir)\r
+ if self.OutputFile:\r
+ for File in self.OutputFile:\r
+ if not os.path.isabs(File):\r
+ File = os.path.join(self.OutputDir, File)\r
+ if os.path.exists(File):\r
+ shutil.copy2(File, FileDir)\r
+\r
+ def AttemptModuleCacheCopy(self):\r
+ if self.IsBinaryModule:\r
+ return False\r
+ FileDir = path.join(GlobalData.gBinCacheSource, self.Arch, self.SourceDir, self.MetaFile.BaseName)\r
+ HashFile = path.join(FileDir, self.Name + '.hash')\r
+ if os.path.exists(HashFile):\r
+ f = open(HashFile, 'r')\r
+ CacheHash = f.read()\r
+ f.close()\r
+ if GlobalData.gModuleHash[self.Arch][self.Name]:\r
+ if CacheHash == GlobalData.gModuleHash[self.Arch][self.Name]:\r
+ for root, dir, files in os.walk(FileDir):\r
+ for f in files:\r
+ if self.Name + '.hash' in f:\r
+ shutil.copy2(HashFile, self.BuildDir)\r
+ else:\r
+ File = path.join(root, f)\r
+ shutil.copy2(File, self.OutputDir)\r
+ if self.Name == "PcdPeim" or self.Name == "PcdDxe":\r
+ CreatePcdDatabaseCode(self, TemplateString(), TemplateString())\r
+ return True\r
+ return False\r
+\r
## Create makefile for the module and its dependent libraries\r
#\r
# @param CreateLibraryMakeFile Flag indicating if or not the makefiles of\r
self._ApplyBuildRule(Lib.Target, TAB_UNKNOWN_FILE)\r
return self._LibraryAutoGenList\r
\r
+ def GenModuleHash(self):\r
+ if self.Arch not in GlobalData.gModuleHash:\r
+ GlobalData.gModuleHash[self.Arch] = {}\r
+ m = hashlib.md5()\r
+ # Add Platform level hash\r
+ m.update(GlobalData.gPlatformHash)\r
+ # Add Package level hash\r
+ if self.DependentPackageList:\r
+ for Pkg in self.DependentPackageList:\r
+ if Pkg.PackageName in GlobalData.gPackageHash[self.Arch]:\r
+ m.update(GlobalData.gPackageHash[self.Arch][Pkg.PackageName])\r
+\r
+ # Add Library hash\r
+ if self.LibraryAutoGenList:\r
+ for Lib in self.LibraryAutoGenList:\r
+ if Lib.Name not in GlobalData.gModuleHash[self.Arch]:\r
+ Lib.GenModuleHash()\r
+ m.update(GlobalData.gModuleHash[self.Arch][Lib.Name])\r
+\r
+ # Add Module self\r
+ f = open(str(self.MetaFile), 'r')\r
+ Content = f.read()\r
+ f.close()\r
+ m.update(Content)\r
+ # Add Module's source files\r
+ if self.SourceFileList:\r
+ for File in self.SourceFileList:\r
+ f = open(str(File), 'r')\r
+ Content = f.read()\r
+ f.close()\r
+ m.update(Content)\r
+\r
+ ModuleHashFile = path.join(self.BuildDir, self.Name + ".hash")\r
+ if self.Name not in GlobalData.gModuleHash[self.Arch]:\r
+ GlobalData.gModuleHash[self.Arch][self.Name] = m.hexdigest()\r
+ if GlobalData.gBinCacheSource:\r
+ CacheValid = self.AttemptModuleCacheCopy()\r
+ if CacheValid:\r
+ return False\r
+ return SaveFileOnChange(ModuleHashFile, m.hexdigest(), True)\r
+\r
+ ## Decide whether we can skip the ModuleAutoGen process\r
+ def CanSkipbyHash(self):\r
+ if GlobalData.gUseHashCache:\r
+ return not self.GenModuleHash()\r
+\r
## Decide whether we can skip the ModuleAutoGen process\r
- # If any source file is newer than the modeule than we cannot skip\r
+ # If any source file is newer than the module than we cannot skip\r
#\r
def CanSkip(self):\r
if not os.path.exists(self.GetTimeStampPath()):\r
GlobalData.BuildOptionPcd = BuildOptions.OptionPcd\r
#Set global flag for build mode\r
GlobalData.gIgnoreSource = BuildOptions.IgnoreSources\r
+ GlobalData.gUseHashCache = BuildOptions.UseHashCache\r
+ GlobalData.gBinCacheDest = BuildOptions.BinCacheDest\r
+ GlobalData.gBinCacheSource = BuildOptions.BinCacheSource\r
+\r
+ if GlobalData.gBinCacheDest and not GlobalData.gUseHashCache:\r
+ EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-destination must be used together with --hash.")\r
+\r
+ if GlobalData.gBinCacheSource and not GlobalData.gUseHashCache:\r
+ EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-source must be used together with --hash.")\r
+\r
+ if GlobalData.gBinCacheDest and GlobalData.gBinCacheSource:\r
+ EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-destination can not be used together with --binary-source.")\r
+\r
+ if GlobalData.gBinCacheSource:\r
+ BinCacheSource = os.path.normpath(GlobalData.gBinCacheSource)\r
+ if not os.path.isabs(BinCacheSource):\r
+ BinCacheSource = mws.join(self.WorkspaceDir, BinCacheSource)\r
+ GlobalData.gBinCacheSource = BinCacheSource\r
+\r
+ if GlobalData.gBinCacheDest:\r
+ BinCacheDest = os.path.normpath(GlobalData.gBinCacheDest)\r
+ if not os.path.isabs(BinCacheDest):\r
+ BinCacheDest = mws.join(self.WorkspaceDir, BinCacheDest)\r
+ GlobalData.gBinCacheDest = BinCacheDest\r
\r
if self.ConfDirectory:\r
# Get alternate Conf location, if it is absolute, then just use the absolute directory name\r
\r
if Ma == None:\r
continue\r
+ if Ma.CanSkipbyHash():\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
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
\r
(Opt, Args) = Parser.parse_args()\r
return (Opt, Args)\r