]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Source/Python/Ecc/Ecc.py
BaseTools/Ecc: Fix a bug to report fake issue
[mirror_edk2.git] / BaseTools / Source / Python / Ecc / Ecc.py
CommitLineData
30fdf114
LG
1## @file\r
2# This file is used to be the main entrance of ECC tool\r
3#\r
e4ac870f 4# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>\r
40d841f6 5# This program and the accompanying materials\r
30fdf114
LG
6# are licensed and made available under the terms and conditions of the BSD License\r
7# which accompanies this distribution. The full text of the license may be found at\r
8# http://opensource.org/licenses/bsd-license.php\r
9#\r
10# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12#\r
13\r
14##\r
15# Import Modules\r
16#\r
1be2ed90 17import Common.LongFilePathOs as os, time, glob, sys\r
30fdf114
LG
18import Common.EdkLogger as EdkLogger\r
19import Database\r
20import EccGlobalData\r
21from MetaDataParser import *\r
22from optparse import OptionParser\r
23from Configuration import Configuration\r
24from Check import Check\r
9508d0fa 25import Common.GlobalData as GlobalData\r
d0acc87a 26\r
30fdf114 27from Common.String import NormPath\r
b36d134f 28from Common.BuildVersion import gBUILD_VERSION\r
30fdf114 29from Common import BuildToolError\r
9508d0fa 30from Common.Misc import PathClass\r
64b2609f 31from Common.Misc import DirCache\r
d0acc87a
LG
32from MetaFileWorkspace.MetaFileParser import DscParser\r
33from MetaFileWorkspace.MetaFileParser import DecParser\r
34from MetaFileWorkspace.MetaFileParser import InfParser\r
35from MetaFileWorkspace.MetaFileParser import Fdf\r
36from MetaFileWorkspace.MetaFileTable import MetaFileStorage\r
30fdf114 37import c\r
fd171542 38import re, string\r
30fdf114 39from Exception import *\r
1be2ed90 40from Common.LongFilePathSupport import OpenLongFilePath as open\r
c4f52e12 41from Common.MultipleWorkspace import MultipleWorkspace as mws\r
30fdf114
LG
42\r
43## Ecc\r
44#\r
45# This class is used to define Ecc main entrance\r
46#\r
47# @param object: Inherited from object class\r
48#\r
49class Ecc(object):\r
50 def __init__(self):\r
51 # Version and Copyright\r
b36d134f 52 self.VersionNumber = ("0.01" + " " + gBUILD_VERSION)\r
30fdf114 53 self.Version = "%prog Version " + self.VersionNumber\r
52302d4d 54 self.Copyright = "Copyright (c) 2009 - 2010, Intel Corporation All rights reserved."\r
30fdf114
LG
55\r
56 self.InitDefaultConfigIni()\r
57 self.OutputFile = 'output.txt'\r
58 self.ReportFile = 'Report.csv'\r
59 self.ExceptionFile = 'exception.xml'\r
60 self.IsInit = True\r
61 self.ScanSourceCode = True\r
62 self.ScanMetaData = True\r
d0acc87a 63 self.MetaFile = ''\r
e4ac870f 64 self.OnlyScan = None\r
fd171542 65\r
30fdf114
LG
66 # Parse the options and args\r
67 self.ParseOption()\r
9508d0fa
LG
68 \r
69 #\r
70 # Check EFI_SOURCE (Edk build convention). EDK_SOURCE will always point to ECP\r
71 #\r
72 WorkspaceDir = os.path.normcase(os.path.normpath(os.environ["WORKSPACE"]))\r
73 os.environ["WORKSPACE"] = WorkspaceDir\r
c4f52e12
LY
74 \r
75 # set multiple workspace\r
76 PackagesPath = os.getenv("PACKAGES_PATH")\r
77 mws.setWs(WorkspaceDir, PackagesPath)\r
78 \r
9508d0fa 79 if "ECP_SOURCE" not in os.environ:\r
c4f52e12 80 os.environ["ECP_SOURCE"] = mws.join(WorkspaceDir, GlobalData.gEdkCompatibilityPkg)\r
9508d0fa
LG
81 if "EFI_SOURCE" not in os.environ:\r
82 os.environ["EFI_SOURCE"] = os.environ["ECP_SOURCE"]\r
83 if "EDK_SOURCE" not in os.environ:\r
84 os.environ["EDK_SOURCE"] = os.environ["ECP_SOURCE"]\r
85\r
86 #\r
87 # Unify case of characters on case-insensitive systems\r
88 #\r
89 EfiSourceDir = os.path.normcase(os.path.normpath(os.environ["EFI_SOURCE"]))\r
90 EdkSourceDir = os.path.normcase(os.path.normpath(os.environ["EDK_SOURCE"]))\r
91 EcpSourceDir = os.path.normcase(os.path.normpath(os.environ["ECP_SOURCE"]))\r
92 \r
93 os.environ["EFI_SOURCE"] = EfiSourceDir\r
94 os.environ["EDK_SOURCE"] = EdkSourceDir\r
95 os.environ["ECP_SOURCE"] = EcpSourceDir\r
96 \r
97 GlobalData.gWorkspace = WorkspaceDir\r
98 GlobalData.gEfiSource = EfiSourceDir\r
99 GlobalData.gEdkSource = EdkSourceDir\r
100 GlobalData.gEcpSource = EcpSourceDir\r
101\r
102 GlobalData.gGlobalDefines["WORKSPACE"] = WorkspaceDir\r
103 GlobalData.gGlobalDefines["EFI_SOURCE"] = EfiSourceDir\r
104 GlobalData.gGlobalDefines["EDK_SOURCE"] = EdkSourceDir\r
105 GlobalData.gGlobalDefines["ECP_SOURCE"] = EcpSourceDir\r
106 \r
107 \r
30fdf114
LG
108 # Generate checkpoints list\r
109 EccGlobalData.gConfig = Configuration(self.ConfigFile)\r
fd171542 110\r
30fdf114
LG
111 # Generate exception list\r
112 EccGlobalData.gException = ExceptionCheck(self.ExceptionFile)\r
fd171542 113\r
30fdf114
LG
114 # Init Ecc database\r
115 EccGlobalData.gDb = Database.Database(Database.DATABASE_PATH)\r
116 EccGlobalData.gDb.InitDatabase(self.IsInit)\r
fd171542 117\r
64b2609f
LG
118 #\r
119 # Get files real name in workspace dir\r
120 #\r
121 GlobalData.gAllFiles = DirCache(GlobalData.gWorkspace)\r
122 \r
30fdf114 123 # Build ECC database\r
e4ac870f
LG
124# self.BuildDatabase()\r
125 self.DetectOnlyScanDirs()\r
126 \r
30fdf114
LG
127 # Start to check\r
128 self.Check()\r
fd171542 129\r
30fdf114
LG
130 # Show report\r
131 self.GenReport()\r
fd171542 132\r
30fdf114
LG
133 # Close Database\r
134 EccGlobalData.gDb.Close()\r
135\r
136 def InitDefaultConfigIni(self):\r
137 paths = map(lambda p: os.path.join(p, 'Ecc', 'config.ini'), sys.path)\r
138 paths = (os.path.realpath('config.ini'),) + tuple(paths)\r
139 for path in paths:\r
140 if os.path.exists(path):\r
141 self.ConfigFile = path\r
142 return\r
143 self.ConfigFile = 'config.ini'\r
144\r
e4ac870f
LG
145\r
146 ## DetectOnlyScan\r
147 #\r
148 # Detect whether only scanned folders have been enabled\r
149 #\r
150 def DetectOnlyScanDirs(self):\r
151 if self.OnlyScan == True:\r
152 OnlyScanDirs = []\r
153 # Use regex here if multiple spaces or TAB exists in ScanOnlyDirList in config.ini file\r
154 for folder in re.finditer(r'\S+', EccGlobalData.gConfig.ScanOnlyDirList):\r
155 OnlyScanDirs.append(folder.group())\r
156 if len(OnlyScanDirs) != 0:\r
157 self.BuildDatabase(OnlyScanDirs)\r
158 else:\r
159 EdkLogger.error("ECC", BuildToolError.OPTION_VALUE_INVALID, ExtraData="Use -f option need to fill specific folders in config.ini file")\r
160 else:\r
161 self.BuildDatabase()\r
162 \r
163 \r
30fdf114
LG
164 ## BuildDatabase\r
165 #\r
166 # Build the database for target\r
167 #\r
e4ac870f 168 def BuildDatabase(self, SpeciDirs = None):\r
30fdf114
LG
169 # Clean report table\r
170 EccGlobalData.gDb.TblReport.Drop()\r
171 EccGlobalData.gDb.TblReport.Create()\r
fd171542 172\r
30fdf114 173 # Build database\r
64b2609f 174 if self.IsInit: \r
30fdf114 175 if self.ScanMetaData:\r
64b2609f 176 EdkLogger.quiet("Building database for Meta Data File ...")\r
e4ac870f 177 self.BuildMetaDataFileDatabase(SpeciDirs)\r
64b2609f
LG
178 if self.ScanSourceCode:\r
179 EdkLogger.quiet("Building database for Meta Data File Done!")\r
e4ac870f
LG
180 if SpeciDirs == None:\r
181 c.CollectSourceCodeDataIntoDB(EccGlobalData.gTarget)\r
182 else:\r
183 for specificDir in SpeciDirs:\r
184 c.CollectSourceCodeDataIntoDB(os.path.join(EccGlobalData.gTarget, specificDir))\r
fd171542 185\r
30fdf114 186 EccGlobalData.gIdentifierTableList = GetTableList((MODEL_FILE_C, MODEL_FILE_H), 'Identifier', EccGlobalData.gDb)\r
e56468c0 187 EccGlobalData.gCFileList = GetFileList(MODEL_FILE_C, EccGlobalData.gDb)\r
188 EccGlobalData.gHFileList = GetFileList(MODEL_FILE_H, EccGlobalData.gDb)\r
b3d07ff8 189 EccGlobalData.gUFileList = GetFileList(MODEL_FILE_UNI, EccGlobalData.gDb)\r
fd171542 190\r
30fdf114
LG
191 ## BuildMetaDataFileDatabase\r
192 #\r
193 # Build the database for meta data files\r
194 #\r
e4ac870f
LG
195 def BuildMetaDataFileDatabase(self, SpecificDirs = None):\r
196 ScanFolders = []\r
197 if SpecificDirs == None:\r
198 ScanFolders.append(EccGlobalData.gTarget)\r
199 else:\r
200 for specificDir in SpecificDirs: \r
201 ScanFolders.append(os.path.join(EccGlobalData.gTarget, specificDir))\r
30fdf114
LG
202 EdkLogger.quiet("Building database for meta data files ...")\r
203 Op = open(EccGlobalData.gConfig.MetaDataFileCheckPathOfGenerateFileList, 'w+')\r
204 #SkipDirs = Read from config file\r
205 SkipDirs = EccGlobalData.gConfig.SkipDirList\r
fd171542 206 SkipDirString = string.join(SkipDirs, '|')\r
e4ac870f
LG
207# p = re.compile(r'.*[\\/](?:%s)[\\/]?.*' % SkipDirString)\r
208 p = re.compile(r'.*[\\/](?:%s^\S)[\\/]?.*' % SkipDirString)\r
209 for scanFolder in ScanFolders:\r
210 for Root, Dirs, Files in os.walk(scanFolder):\r
211 if p.match(Root.upper()):\r
30fdf114 212 continue\r
e4ac870f
LG
213 for Dir in Dirs:\r
214 Dirname = os.path.join(Root, Dir)\r
215 if os.path.islink(Dirname):\r
216 Dirname = os.path.realpath(Dirname)\r
217 if os.path.isdir(Dirname):\r
218 # symlinks to directories are treated as directories\r
219 Dirs.remove(Dir)\r
220 Dirs.append(Dirname)\r
221 \r
222 for File in Files:\r
223 if len(File) > 4 and File[-4:].upper() == ".DEC":\r
224 Filename = os.path.normpath(os.path.join(Root, File))\r
225 EdkLogger.quiet("Parsing %s" % Filename)\r
226 Op.write("%s\r" % Filename)\r
227 #Dec(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)\r
228 self.MetaFile = DecParser(Filename, MODEL_FILE_DEC, EccGlobalData.gDb.TblDec)\r
229 self.MetaFile.Start()\r
230 continue\r
231 if len(File) > 4 and File[-4:].upper() == ".DSC":\r
232 Filename = os.path.normpath(os.path.join(Root, File))\r
233 EdkLogger.quiet("Parsing %s" % Filename)\r
234 Op.write("%s\r" % Filename)\r
235 #Dsc(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)\r
236 self.MetaFile = DscParser(PathClass(Filename, Root), MODEL_FILE_DSC, MetaFileStorage(EccGlobalData.gDb.TblDsc.Cur, Filename, MODEL_FILE_DSC, True))\r
237 # alwasy do post-process, in case of macros change\r
238 self.MetaFile.DoPostProcess()\r
239 self.MetaFile.Start()\r
240 self.MetaFile._PostProcess()\r
241 continue\r
242 if len(File) > 4 and File[-4:].upper() == ".INF":\r
243 Filename = os.path.normpath(os.path.join(Root, File))\r
244 EdkLogger.quiet("Parsing %s" % Filename)\r
245 Op.write("%s\r" % Filename)\r
246 #Inf(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)\r
247 self.MetaFile = InfParser(Filename, MODEL_FILE_INF, EccGlobalData.gDb.TblInf)\r
248 self.MetaFile.Start()\r
249 continue\r
250 if len(File) > 4 and File[-4:].upper() == ".FDF":\r
251 Filename = os.path.normpath(os.path.join(Root, File))\r
252 EdkLogger.quiet("Parsing %s" % Filename)\r
253 Op.write("%s\r" % Filename)\r
254 Fdf(Filename, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)\r
255 continue\r
b3d07ff8
HC
256 if len(File) > 4 and File[-4:].upper() == ".UNI":\r
257 Filename = os.path.normpath(os.path.join(Root, File))\r
258 EdkLogger.quiet("Parsing %s" % Filename)\r
259 Op.write("%s\r" % Filename)\r
1b2467c5
HC
260 FileID = EccGlobalData.gDb.TblFile.InsertFile(Filename, MODEL_FILE_UNI)\r
261 EccGlobalData.gDb.TblReport.UpdateBelongsToItemByFile(FileID, File)\r
b3d07ff8
HC
262 continue\r
263\r
30fdf114 264 Op.close()\r
fd171542 265\r
30fdf114
LG
266 # Commit to database\r
267 EccGlobalData.gDb.Conn.commit()\r
fd171542 268\r
30fdf114 269 EdkLogger.quiet("Building database for meta data files done!")\r
fd171542 270\r
30fdf114
LG
271 ##\r
272 #\r
273 # Check each checkpoint\r
274 #\r
275 def Check(self):\r
276 EdkLogger.quiet("Checking ...")\r
277 EccCheck = Check()\r
278 EccCheck.Check()\r
279 EdkLogger.quiet("Checking done!")\r
fd171542 280\r
30fdf114
LG
281 ##\r
282 #\r
283 # Generate the scan report\r
284 #\r
285 def GenReport(self):\r
286 EdkLogger.quiet("Generating report ...")\r
287 EccGlobalData.gDb.TblReport.ToCSV(self.ReportFile)\r
288 EdkLogger.quiet("Generating report done!")\r
fd171542 289\r
30fdf114
LG
290 def GetRealPathCase(self, path):\r
291 TmpPath = path.rstrip(os.sep)\r
292 PathParts = TmpPath.split(os.sep)\r
293 if len(PathParts) == 0:\r
294 return path\r
295 if len(PathParts) == 1:\r
296 if PathParts[0].strip().endswith(':'):\r
297 return PathParts[0].upper()\r
298 # Relative dir, list . current dir\r
299 Dirs = os.listdir('.')\r
300 for Dir in Dirs:\r
301 if Dir.upper() == PathParts[0].upper():\r
302 return Dir\r
fd171542 303\r
30fdf114
LG
304 if PathParts[0].strip().endswith(':'):\r
305 PathParts[0] = PathParts[0].upper()\r
306 ParentDir = PathParts[0]\r
307 RealPath = ParentDir\r
308 if PathParts[0] == '':\r
309 RealPath = os.sep\r
310 ParentDir = os.sep\r
fd171542 311\r
30fdf114
LG
312 PathParts.remove(PathParts[0]) # need to remove the parent\r
313 for Part in PathParts:\r
314 Dirs = os.listdir(ParentDir + os.sep)\r
315 for Dir in Dirs:\r
316 if Dir.upper() == Part.upper():\r
317 RealPath += os.sep\r
318 RealPath += Dir\r
319 break\r
320 ParentDir += os.sep\r
321 ParentDir += Dir\r
fd171542 322\r
30fdf114 323 return RealPath\r
fd171542 324\r
30fdf114
LG
325 ## ParseOption\r
326 #\r
327 # Parse options\r
328 #\r
329 def ParseOption(self):\r
330 EdkLogger.quiet("Loading ECC configuration ... done")\r
331 (Options, Target) = self.EccOptionParser()\r
fd171542 332\r
52302d4d
LG
333 if Options.Workspace:\r
334 os.environ["WORKSPACE"] = Options.Workspace\r
e56468c0 335\r
30fdf114
LG
336 # Check workspace envirnoment\r
337 if "WORKSPACE" not in os.environ:\r
fd171542 338 EdkLogger.error("ECC", BuildToolError.ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
30fdf114
LG
339 ExtraData="WORKSPACE")\r
340 else:\r
341 EccGlobalData.gWorkspace = os.path.normpath(os.getenv("WORKSPACE"))\r
342 if not os.path.exists(EccGlobalData.gWorkspace):\r
343 EdkLogger.error("ECC", BuildToolError.FILE_NOT_FOUND, ExtraData="WORKSPACE = %s" % EccGlobalData.gWorkspace)\r
344 os.environ["WORKSPACE"] = EccGlobalData.gWorkspace\r
345 # Set log level\r
346 self.SetLogLevel(Options)\r
fd171542 347\r
30fdf114
LG
348 # Set other options\r
349 if Options.ConfigFile != None:\r
350 self.ConfigFile = Options.ConfigFile\r
351 if Options.OutputFile != None:\r
352 self.OutputFile = Options.OutputFile\r
353 if Options.ReportFile != None:\r
354 self.ReportFile = Options.ReportFile\r
52302d4d
LG
355 if Options.ExceptionFile != None:\r
356 self.ExceptionFile = Options.ExceptionFile\r
30fdf114
LG
357 if Options.Target != None:\r
358 if not os.path.isdir(Options.Target):\r
359 EdkLogger.error("ECC", BuildToolError.OPTION_VALUE_INVALID, ExtraData="Target [%s] does NOT exist" % Options.Target)\r
360 else:\r
361 EccGlobalData.gTarget = self.GetRealPathCase(os.path.normpath(Options.Target))\r
362 else:\r
363 EdkLogger.warn("Ecc", EdkLogger.ECC_ERROR, "The target source tree was not specified, using current WORKSPACE instead!")\r
364 EccGlobalData.gTarget = os.path.normpath(os.getenv("WORKSPACE"))\r
365 if Options.keepdatabase != None:\r
366 self.IsInit = False\r
367 if Options.metadata != None and Options.sourcecode != None:\r
368 EdkLogger.error("ECC", BuildToolError.OPTION_CONFLICT, ExtraData="-m and -s can't be specified at one time")\r
369 if Options.metadata != None:\r
370 self.ScanSourceCode = False\r
371 if Options.sourcecode != None:\r
372 self.ScanMetaData = False\r
e4ac870f
LG
373 if Options.folders != None:\r
374 self.OnlyScan = True\r
fd171542 375\r
30fdf114
LG
376 ## SetLogLevel\r
377 #\r
378 # Set current log level of the tool based on args\r
379 #\r
fd171542 380 # @param Option: The option list including log level setting\r
30fdf114
LG
381 #\r
382 def SetLogLevel(self, Option):\r
383 if Option.verbose != None:\r
384 EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
385 elif Option.quiet != None:\r
386 EdkLogger.SetLevel(EdkLogger.QUIET)\r
387 elif Option.debug != None:\r
388 EdkLogger.SetLevel(Option.debug + 1)\r
389 else:\r
390 EdkLogger.SetLevel(EdkLogger.INFO)\r
391\r
392 ## Parse command line options\r
393 #\r
394 # Using standard Python module optparse to parse command line option of this tool.\r
395 #\r
396 # @retval Opt A optparse.Values object containing the parsed options\r
397 # @retval Args Target of build command\r
398 #\r
399 def EccOptionParser(self):\r
400 Parser = OptionParser(description = self.Copyright, version = self.Version, prog = "Ecc.exe", usage = "%prog [options]")\r
401 Parser.add_option("-t", "--target sourcepath", action="store", type="string", dest='Target',\r
402 help="Check all files under the target workspace.")\r
403 Parser.add_option("-c", "--config filename", action="store", type="string", dest="ConfigFile",\r
404 help="Specify a configuration file. Defaultly use config.ini under ECC tool directory.")\r
405 Parser.add_option("-o", "--outfile filename", action="store", type="string", dest="OutputFile",\r
406 help="Specify the name of an output file, if and only if one filename was specified.")\r
407 Parser.add_option("-r", "--reportfile filename", action="store", type="string", dest="ReportFile",\r
408 help="Specify the name of an report file, if and only if one filename was specified.")\r
52302d4d
LG
409 Parser.add_option("-e", "--exceptionfile filename", action="store", type="string", dest="ExceptionFile",\r
410 help="Specify the name of an exception file, if and only if one filename was specified.")\r
30fdf114
LG
411 Parser.add_option("-m", "--metadata", action="store_true", type=None, help="Only scan meta-data files information if this option is specified.")\r
412 Parser.add_option("-s", "--sourcecode", action="store_true", type=None, help="Only scan source code files information if this option is specified.")\r
413 Parser.add_option("-k", "--keepdatabase", action="store_true", type=None, help="The existing Ecc database will not be cleaned except report information if this option is specified.")\r
fd171542 414 Parser.add_option("-l", "--log filename", action="store", dest="LogFile", help="""If specified, the tool should emit the changes that\r
415 were made by the tool after printing the result message.\r
416 If filename, the emit to the file, otherwise emit to\r
417 standard output. If no modifications were made, then do not\r
30fdf114
LG
418 create a log file, or output a log message.""")\r
419 Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
420 Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
421 "including library instances selected, final dependency expression, "\\r
422 "and warning messages, etc.")\r
423 Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
52302d4d 424 Parser.add_option("-w", "--workspace", action="store", type="string", dest='Workspace', help="Specify workspace.")\r
e4ac870f 425 Parser.add_option("-f", "--folders", action="store_true", type=None, help="Only scanning specified folders which are recorded in config.ini file.")\r
fd171542 426\r
30fdf114 427 (Opt, Args)=Parser.parse_args()\r
fd171542 428\r
30fdf114
LG
429 return (Opt, Args)\r
430\r
431##\r
432#\r
433# This acts like the main() function for the script, unless it is 'import'ed into another\r
434# script.\r
435#\r
436if __name__ == '__main__':\r
437 # Initialize log system\r
438 EdkLogger.Initialize()\r
439 EdkLogger.IsRaiseError = False\r
440 EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[00:00]" + "\n")\r
441\r
442 StartTime = time.clock()\r
443 Ecc = Ecc()\r
444 FinishTime = time.clock()\r
445\r
446 BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime))))\r
447 EdkLogger.quiet("\n%s [%s]" % (time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration))\r