2 # This file is used to be the main entrance of ECC tool
4 # Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
5 # This program and the accompanying materials
6 # are licensed and made available under the terms and conditions of the BSD License
7 # which accompanies this distribution. The full text of the license may be found at
8 # http://opensource.org/licenses/bsd-license.php
10 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 import os
, time
, glob
, sys
18 import Common
.EdkLogger
as EdkLogger
21 from MetaDataParser
import *
22 from optparse
import OptionParser
23 from Configuration
import Configuration
24 from Check
import Check
25 import Common
.GlobalData
as GlobalData
27 from Common
.String
import NormPath
28 from Common
.BuildVersion
import gBUILD_VERSION
29 from Common
import BuildToolError
30 from Common
.Misc
import PathClass
31 from Common
.Misc
import DirCache
32 from MetaFileWorkspace
.MetaFileParser
import DscParser
33 from MetaFileWorkspace
.MetaFileParser
import DecParser
34 from MetaFileWorkspace
.MetaFileParser
import InfParser
35 from MetaFileWorkspace
.MetaFileParser
import Fdf
36 from MetaFileWorkspace
.MetaFileTable
import MetaFileStorage
39 from Exception import *
43 # This class is used to define Ecc main entrance
45 # @param object: Inherited from object class
49 # Version and Copyright
50 self
.VersionNumber
= ("0.01" + " " + gBUILD_VERSION
)
51 self
.Version
= "%prog Version " + self
.VersionNumber
52 self
.Copyright
= "Copyright (c) 2009 - 2010, Intel Corporation All rights reserved."
54 self
.InitDefaultConfigIni()
55 self
.OutputFile
= 'output.txt'
56 self
.ReportFile
= 'Report.csv'
57 self
.ExceptionFile
= 'exception.xml'
59 self
.ScanSourceCode
= True
60 self
.ScanMetaData
= True
64 # Parse the options and args
68 # Check EFI_SOURCE (Edk build convention). EDK_SOURCE will always point to ECP
70 WorkspaceDir
= os
.path
.normcase(os
.path
.normpath(os
.environ
["WORKSPACE"]))
71 os
.environ
["WORKSPACE"] = WorkspaceDir
72 if "ECP_SOURCE" not in os
.environ
:
73 os
.environ
["ECP_SOURCE"] = os
.path
.join(WorkspaceDir
, GlobalData
.gEdkCompatibilityPkg
)
74 if "EFI_SOURCE" not in os
.environ
:
75 os
.environ
["EFI_SOURCE"] = os
.environ
["ECP_SOURCE"]
76 if "EDK_SOURCE" not in os
.environ
:
77 os
.environ
["EDK_SOURCE"] = os
.environ
["ECP_SOURCE"]
80 # Unify case of characters on case-insensitive systems
82 EfiSourceDir
= os
.path
.normcase(os
.path
.normpath(os
.environ
["EFI_SOURCE"]))
83 EdkSourceDir
= os
.path
.normcase(os
.path
.normpath(os
.environ
["EDK_SOURCE"]))
84 EcpSourceDir
= os
.path
.normcase(os
.path
.normpath(os
.environ
["ECP_SOURCE"]))
86 os
.environ
["EFI_SOURCE"] = EfiSourceDir
87 os
.environ
["EDK_SOURCE"] = EdkSourceDir
88 os
.environ
["ECP_SOURCE"] = EcpSourceDir
90 GlobalData
.gWorkspace
= WorkspaceDir
91 GlobalData
.gEfiSource
= EfiSourceDir
92 GlobalData
.gEdkSource
= EdkSourceDir
93 GlobalData
.gEcpSource
= EcpSourceDir
95 GlobalData
.gGlobalDefines
["WORKSPACE"] = WorkspaceDir
96 GlobalData
.gGlobalDefines
["EFI_SOURCE"] = EfiSourceDir
97 GlobalData
.gGlobalDefines
["EDK_SOURCE"] = EdkSourceDir
98 GlobalData
.gGlobalDefines
["ECP_SOURCE"] = EcpSourceDir
101 # Generate checkpoints list
102 EccGlobalData
.gConfig
= Configuration(self
.ConfigFile
)
104 # Generate exception list
105 EccGlobalData
.gException
= ExceptionCheck(self
.ExceptionFile
)
108 EccGlobalData
.gDb
= Database
.Database(Database
.DATABASE_PATH
)
109 EccGlobalData
.gDb
.InitDatabase(self
.IsInit
)
112 # Get files real name in workspace dir
114 GlobalData
.gAllFiles
= DirCache(GlobalData
.gWorkspace
)
117 # self.BuildDatabase()
118 self
.DetectOnlyScanDirs()
127 EccGlobalData
.gDb
.Close()
129 def InitDefaultConfigIni(self
):
130 paths
= map(lambda p
: os
.path
.join(p
, 'Ecc', 'config.ini'), sys
.path
)
131 paths
= (os
.path
.realpath('config.ini'),) + tuple(paths
)
133 if os
.path
.exists(path
):
134 self
.ConfigFile
= path
136 self
.ConfigFile
= 'config.ini'
141 # Detect whether only scanned folders have been enabled
143 def DetectOnlyScanDirs(self
):
144 if self
.OnlyScan
== True:
146 # Use regex here if multiple spaces or TAB exists in ScanOnlyDirList in config.ini file
147 for folder
in re
.finditer(r
'\S+', EccGlobalData
.gConfig
.ScanOnlyDirList
):
148 OnlyScanDirs
.append(folder
.group())
149 if len(OnlyScanDirs
) != 0:
150 self
.BuildDatabase(OnlyScanDirs
)
152 EdkLogger
.error("ECC", BuildToolError
.OPTION_VALUE_INVALID
, ExtraData
="Use -f option need to fill specific folders in config.ini file")
159 # Build the database for target
161 def BuildDatabase(self
, SpeciDirs
= None):
163 EccGlobalData
.gDb
.TblReport
.Drop()
164 EccGlobalData
.gDb
.TblReport
.Create()
168 if self
.ScanMetaData
:
169 EdkLogger
.quiet("Building database for Meta Data File ...")
170 self
.BuildMetaDataFileDatabase(SpeciDirs
)
171 if self
.ScanSourceCode
:
172 EdkLogger
.quiet("Building database for Meta Data File Done!")
173 if SpeciDirs
== None:
174 c
.CollectSourceCodeDataIntoDB(EccGlobalData
.gTarget
)
176 for specificDir
in SpeciDirs
:
177 c
.CollectSourceCodeDataIntoDB(os
.path
.join(EccGlobalData
.gTarget
, specificDir
))
179 EccGlobalData
.gIdentifierTableList
= GetTableList((MODEL_FILE_C
, MODEL_FILE_H
), 'Identifier', EccGlobalData
.gDb
)
180 EccGlobalData
.gCFileList
= GetFileList(MODEL_FILE_C
, EccGlobalData
.gDb
)
181 EccGlobalData
.gHFileList
= GetFileList(MODEL_FILE_H
, EccGlobalData
.gDb
)
183 ## BuildMetaDataFileDatabase
185 # Build the database for meta data files
187 def BuildMetaDataFileDatabase(self
, SpecificDirs
= None):
189 if SpecificDirs
== None:
190 ScanFolders
.append(EccGlobalData
.gTarget
)
192 for specificDir
in SpecificDirs
:
193 ScanFolders
.append(os
.path
.join(EccGlobalData
.gTarget
, specificDir
))
194 EdkLogger
.quiet("Building database for meta data files ...")
195 Op
= open(EccGlobalData
.gConfig
.MetaDataFileCheckPathOfGenerateFileList
, 'w+')
196 #SkipDirs = Read from config file
197 SkipDirs
= EccGlobalData
.gConfig
.SkipDirList
198 SkipDirString
= string
.join(SkipDirs
, '|')
199 # p = re.compile(r'.*[\\/](?:%s)[\\/]?.*' % SkipDirString)
200 p
= re
.compile(r
'.*[\\/](?:%s^\S)[\\/]?.*' % SkipDirString
)
201 for scanFolder
in ScanFolders
:
202 for Root
, Dirs
, Files
in os
.walk(scanFolder
):
203 if p
.match(Root
.upper()):
206 Dirname
= os
.path
.join(Root
, Dir
)
207 if os
.path
.islink(Dirname
):
208 Dirname
= os
.path
.realpath(Dirname
)
209 if os
.path
.isdir(Dirname
):
210 # symlinks to directories are treated as directories
215 if len(File
) > 4 and File
[-4:].upper() == ".DEC":
216 Filename
= os
.path
.normpath(os
.path
.join(Root
, File
))
217 EdkLogger
.quiet("Parsing %s" % Filename
)
218 Op
.write("%s\r" % Filename
)
219 #Dec(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)
220 self
.MetaFile
= DecParser(Filename
, MODEL_FILE_DEC
, EccGlobalData
.gDb
.TblDec
)
221 self
.MetaFile
.Start()
223 if len(File
) > 4 and File
[-4:].upper() == ".DSC":
224 Filename
= os
.path
.normpath(os
.path
.join(Root
, File
))
225 EdkLogger
.quiet("Parsing %s" % Filename
)
226 Op
.write("%s\r" % Filename
)
227 #Dsc(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)
228 self
.MetaFile
= DscParser(PathClass(Filename
, Root
), MODEL_FILE_DSC
, MetaFileStorage(EccGlobalData
.gDb
.TblDsc
.Cur
, Filename
, MODEL_FILE_DSC
, True))
229 # alwasy do post-process, in case of macros change
230 self
.MetaFile
.DoPostProcess()
231 self
.MetaFile
.Start()
232 self
.MetaFile
._PostProcess
()
234 if len(File
) > 4 and File
[-4:].upper() == ".INF":
235 Filename
= os
.path
.normpath(os
.path
.join(Root
, File
))
236 EdkLogger
.quiet("Parsing %s" % Filename
)
237 Op
.write("%s\r" % Filename
)
238 #Inf(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)
239 self
.MetaFile
= InfParser(Filename
, MODEL_FILE_INF
, EccGlobalData
.gDb
.TblInf
)
240 self
.MetaFile
.Start()
242 if len(File
) > 4 and File
[-4:].upper() == ".FDF":
243 Filename
= os
.path
.normpath(os
.path
.join(Root
, File
))
244 EdkLogger
.quiet("Parsing %s" % Filename
)
245 Op
.write("%s\r" % Filename
)
246 Fdf(Filename
, True, EccGlobalData
.gWorkspace
, EccGlobalData
.gDb
)
251 EccGlobalData
.gDb
.Conn
.commit()
253 EdkLogger
.quiet("Building database for meta data files done!")
257 # Check each checkpoint
260 EdkLogger
.quiet("Checking ...")
263 EdkLogger
.quiet("Checking done!")
267 # Generate the scan report
270 EdkLogger
.quiet("Generating report ...")
271 EccGlobalData
.gDb
.TblReport
.ToCSV(self
.ReportFile
)
272 EdkLogger
.quiet("Generating report done!")
274 def GetRealPathCase(self
, path
):
275 TmpPath
= path
.rstrip(os
.sep
)
276 PathParts
= TmpPath
.split(os
.sep
)
277 if len(PathParts
) == 0:
279 if len(PathParts
) == 1:
280 if PathParts
[0].strip().endswith(':'):
281 return PathParts
[0].upper()
282 # Relative dir, list . current dir
283 Dirs
= os
.listdir('.')
285 if Dir
.upper() == PathParts
[0].upper():
288 if PathParts
[0].strip().endswith(':'):
289 PathParts
[0] = PathParts
[0].upper()
290 ParentDir
= PathParts
[0]
292 if PathParts
[0] == '':
296 PathParts
.remove(PathParts
[0]) # need to remove the parent
297 for Part
in PathParts
:
298 Dirs
= os
.listdir(ParentDir
+ os
.sep
)
300 if Dir
.upper() == Part
.upper():
313 def ParseOption(self
):
314 EdkLogger
.quiet("Loading ECC configuration ... done")
315 (Options
, Target
) = self
.EccOptionParser()
317 if Options
.Workspace
:
318 os
.environ
["WORKSPACE"] = Options
.Workspace
320 # Check workspace envirnoment
321 if "WORKSPACE" not in os
.environ
:
322 EdkLogger
.error("ECC", BuildToolError
.ATTRIBUTE_NOT_AVAILABLE
, "Environment variable not found",
323 ExtraData
="WORKSPACE")
325 EccGlobalData
.gWorkspace
= os
.path
.normpath(os
.getenv("WORKSPACE"))
326 if not os
.path
.exists(EccGlobalData
.gWorkspace
):
327 EdkLogger
.error("ECC", BuildToolError
.FILE_NOT_FOUND
, ExtraData
="WORKSPACE = %s" % EccGlobalData
.gWorkspace
)
328 os
.environ
["WORKSPACE"] = EccGlobalData
.gWorkspace
330 self
.SetLogLevel(Options
)
333 if Options
.ConfigFile
!= None:
334 self
.ConfigFile
= Options
.ConfigFile
335 if Options
.OutputFile
!= None:
336 self
.OutputFile
= Options
.OutputFile
337 if Options
.ReportFile
!= None:
338 self
.ReportFile
= Options
.ReportFile
339 if Options
.ExceptionFile
!= None:
340 self
.ExceptionFile
= Options
.ExceptionFile
341 if Options
.Target
!= None:
342 if not os
.path
.isdir(Options
.Target
):
343 EdkLogger
.error("ECC", BuildToolError
.OPTION_VALUE_INVALID
, ExtraData
="Target [%s] does NOT exist" % Options
.Target
)
345 EccGlobalData
.gTarget
= self
.GetRealPathCase(os
.path
.normpath(Options
.Target
))
347 EdkLogger
.warn("Ecc", EdkLogger
.ECC_ERROR
, "The target source tree was not specified, using current WORKSPACE instead!")
348 EccGlobalData
.gTarget
= os
.path
.normpath(os
.getenv("WORKSPACE"))
349 if Options
.keepdatabase
!= None:
351 if Options
.metadata
!= None and Options
.sourcecode
!= None:
352 EdkLogger
.error("ECC", BuildToolError
.OPTION_CONFLICT
, ExtraData
="-m and -s can't be specified at one time")
353 if Options
.metadata
!= None:
354 self
.ScanSourceCode
= False
355 if Options
.sourcecode
!= None:
356 self
.ScanMetaData
= False
357 if Options
.folders
!= None:
362 # Set current log level of the tool based on args
364 # @param Option: The option list including log level setting
366 def SetLogLevel(self
, Option
):
367 if Option
.verbose
!= None:
368 EdkLogger
.SetLevel(EdkLogger
.VERBOSE
)
369 elif Option
.quiet
!= None:
370 EdkLogger
.SetLevel(EdkLogger
.QUIET
)
371 elif Option
.debug
!= None:
372 EdkLogger
.SetLevel(Option
.debug
+ 1)
374 EdkLogger
.SetLevel(EdkLogger
.INFO
)
376 ## Parse command line options
378 # Using standard Python module optparse to parse command line option of this tool.
380 # @retval Opt A optparse.Values object containing the parsed options
381 # @retval Args Target of build command
383 def EccOptionParser(self
):
384 Parser
= OptionParser(description
= self
.Copyright
, version
= self
.Version
, prog
= "Ecc.exe", usage
= "%prog [options]")
385 Parser
.add_option("-t", "--target sourcepath", action
="store", type="string", dest
='Target',
386 help="Check all files under the target workspace.")
387 Parser
.add_option("-c", "--config filename", action
="store", type="string", dest
="ConfigFile",
388 help="Specify a configuration file. Defaultly use config.ini under ECC tool directory.")
389 Parser
.add_option("-o", "--outfile filename", action
="store", type="string", dest
="OutputFile",
390 help="Specify the name of an output file, if and only if one filename was specified.")
391 Parser
.add_option("-r", "--reportfile filename", action
="store", type="string", dest
="ReportFile",
392 help="Specify the name of an report file, if and only if one filename was specified.")
393 Parser
.add_option("-e", "--exceptionfile filename", action
="store", type="string", dest
="ExceptionFile",
394 help="Specify the name of an exception file, if and only if one filename was specified.")
395 Parser
.add_option("-m", "--metadata", action
="store_true", type=None, help="Only scan meta-data files information if this option is specified.")
396 Parser
.add_option("-s", "--sourcecode", action
="store_true", type=None, help="Only scan source code files information if this option is specified.")
397 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.")
398 Parser
.add_option("-l", "--log filename", action
="store", dest
="LogFile", help="""If specified, the tool should emit the changes that
399 were made by the tool after printing the result message.
400 If filename, the emit to the file, otherwise emit to
401 standard output. If no modifications were made, then do not
402 create a log file, or output a log message.""")
403 Parser
.add_option("-q", "--quiet", action
="store_true", type=None, help="Disable all messages except FATAL ERRORS.")
404 Parser
.add_option("-v", "--verbose", action
="store_true", type=None, help="Turn on verbose output with informational messages printed, "\
405 "including library instances selected, final dependency expression, "\
406 "and warning messages, etc.")
407 Parser
.add_option("-d", "--debug", action
="store", type="int", help="Enable debug messages at specified level.")
408 Parser
.add_option("-w", "--workspace", action
="store", type="string", dest
='Workspace', help="Specify workspace.")
409 Parser
.add_option("-f", "--folders", action
="store_true", type=None, help="Only scanning specified folders which are recorded in config.ini file.")
411 (Opt
, Args
)=Parser
.parse_args()
417 # This acts like the main() function for the script, unless it is 'import'ed into another
420 if __name__
== '__main__':
421 # Initialize log system
422 EdkLogger
.Initialize()
423 EdkLogger
.IsRaiseError
= False
424 EdkLogger
.quiet(time
.strftime("%H:%M:%S, %b.%d %Y ", time
.localtime()) + "[00:00]" + "\n")
426 StartTime
= time
.clock()
428 FinishTime
= time
.clock()
430 BuildDuration
= time
.strftime("%M:%S", time
.gmtime(int(round(FinishTime
- StartTime
))))
431 EdkLogger
.quiet("\n%s [%s]" % (time
.strftime("%H:%M:%S, %b.%d %Y", time
.localtime()), BuildDuration
))