2 # This file is used to be the main entrance of ECC tool
4 # Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
5 # Copyright (c) Microsoft Corporation.<BR>
6 # SPDX-License-Identifier: BSD-2-Clause-Patent
12 from __future__
import absolute_import
13 import Common
.LongFilePathOs
as os
, time
, glob
, sys
14 import Common
.EdkLogger
as EdkLogger
15 from Ecc
import Database
16 from Ecc
import EccGlobalData
17 from Ecc
.MetaDataParser
import *
18 from optparse
import OptionParser
19 from Ecc
.Configuration
import Configuration
20 from Ecc
.Check
import Check
21 import Common
.GlobalData
as GlobalData
23 from Common
.StringUtils
import NormPath
24 from Common
.BuildVersion
import gBUILD_VERSION
25 from Common
import BuildToolError
26 from Common
.Misc
import PathClass
27 from Common
.Misc
import DirCache
28 from Ecc
.MetaFileWorkspace
.MetaFileParser
import DscParser
29 from Ecc
.MetaFileWorkspace
.MetaFileParser
import DecParser
30 from Ecc
.MetaFileWorkspace
.MetaFileParser
import InfParser
31 from Ecc
.MetaFileWorkspace
.MetaFileParser
import Fdf
32 from Ecc
.MetaFileWorkspace
.MetaFileTable
import MetaFileStorage
35 from Ecc
.Exception import *
36 from Common
.LongFilePathSupport
import OpenLongFilePath
as open
37 from Common
.MultipleWorkspace
import MultipleWorkspace
as mws
41 # This class is used to define Ecc main entrance
43 # @param object: Inherited from object class
47 # Version and Copyright
48 self
.VersionNumber
= ("1.0" + " Build " + gBUILD_VERSION
)
49 self
.Version
= "%prog Version " + self
.VersionNumber
50 self
.Copyright
= "Copyright (c) 2009 - 2018, Intel Corporation All rights reserved."
52 self
.InitDefaultConfigIni()
53 self
.OutputFile
= 'output.txt'
54 self
.ReportFile
= 'Report.csv'
55 self
.ExceptionFile
= 'exception.xml'
57 self
.ScanSourceCode
= True
58 self
.ScanMetaData
= True
62 # Parse the options and args
64 EdkLogger
.info(time
.strftime("%H:%M:%S, %b.%d %Y ", time
.localtime()) + "[00:00]" + "\n")
66 WorkspaceDir
= os
.path
.normcase(os
.path
.normpath(os
.environ
["WORKSPACE"]))
67 os
.environ
["WORKSPACE"] = WorkspaceDir
69 # set multiple workspace
70 PackagesPath
= os
.getenv("PACKAGES_PATH")
71 mws
.setWs(WorkspaceDir
, PackagesPath
)
73 GlobalData
.gWorkspace
= WorkspaceDir
75 GlobalData
.gGlobalDefines
["WORKSPACE"] = WorkspaceDir
77 EdkLogger
.info("Loading ECC configuration ... done")
78 # Generate checkpoints list
79 EccGlobalData
.gConfig
= Configuration(self
.ConfigFile
)
81 # Generate exception list
82 EccGlobalData
.gException
= ExceptionCheck(self
.ExceptionFile
)
85 EccGlobalData
.gDb
= Database
.Database(Database
.DATABASE_PATH
)
86 EccGlobalData
.gDb
.InitDatabase(self
.IsInit
)
89 # Get files real name in workspace dir
91 GlobalData
.gAllFiles
= DirCache(GlobalData
.gWorkspace
)
94 # self.BuildDatabase()
95 self
.DetectOnlyScanDirs()
104 EccGlobalData
.gDb
.Close()
106 def InitDefaultConfigIni(self
):
107 paths
= map(lambda p
: os
.path
.join(p
, 'Ecc', 'config.ini'), sys
.path
)
108 paths
= (os
.path
.abspath('config.ini'),) + tuple(paths
)
110 if os
.path
.exists(path
):
111 self
.ConfigFile
= path
113 self
.ConfigFile
= 'config.ini'
118 # Detect whether only scanned folders have been enabled
120 def DetectOnlyScanDirs(self
):
121 if self
.OnlyScan
== True:
123 # Use regex here if multiple spaces or TAB exists in ScanOnlyDirList in config.ini file
124 for folder
in re
.finditer(r
'\S+', EccGlobalData
.gConfig
.ScanOnlyDirList
):
125 OnlyScanDirs
.append(folder
.group())
126 if len(OnlyScanDirs
) != 0:
127 self
.BuildDatabase(OnlyScanDirs
)
129 EdkLogger
.error("ECC", BuildToolError
.OPTION_VALUE_INVALID
, ExtraData
="Use -f option need to fill specific folders in config.ini file")
136 # Build the database for target
138 def BuildDatabase(self
, SpeciDirs
= None):
140 EccGlobalData
.gDb
.TblReport
.Drop()
141 EccGlobalData
.gDb
.TblReport
.Create()
145 if self
.ScanMetaData
:
146 EdkLogger
.quiet("Building database for Meta Data File ...")
147 self
.BuildMetaDataFileDatabase(SpeciDirs
)
148 if self
.ScanSourceCode
:
149 EdkLogger
.quiet("Building database for Meta Data File Done!")
150 if SpeciDirs
is None:
151 c
.CollectSourceCodeDataIntoDB(EccGlobalData
.gTarget
)
153 for specificDir
in SpeciDirs
:
154 c
.CollectSourceCodeDataIntoDB(os
.path
.join(EccGlobalData
.gTarget
, specificDir
))
156 EccGlobalData
.gIdentifierTableList
= GetTableList((MODEL_FILE_C
, MODEL_FILE_H
), 'Identifier', EccGlobalData
.gDb
)
157 EccGlobalData
.gCFileList
= GetFileList(MODEL_FILE_C
, EccGlobalData
.gDb
)
158 EccGlobalData
.gHFileList
= GetFileList(MODEL_FILE_H
, EccGlobalData
.gDb
)
159 EccGlobalData
.gUFileList
= GetFileList(MODEL_FILE_UNI
, EccGlobalData
.gDb
)
161 ## BuildMetaDataFileDatabase
163 # Build the database for meta data files
165 def BuildMetaDataFileDatabase(self
, SpecificDirs
= None):
167 if SpecificDirs
is None:
168 ScanFolders
.append(EccGlobalData
.gTarget
)
170 for specificDir
in SpecificDirs
:
171 ScanFolders
.append(os
.path
.join(EccGlobalData
.gTarget
, specificDir
))
172 EdkLogger
.quiet("Building database for meta data files ...")
173 Op
= open(EccGlobalData
.gConfig
.MetaDataFileCheckPathOfGenerateFileList
, 'w+')
174 #SkipDirs = Read from config file
175 SkipDirs
= EccGlobalData
.gConfig
.SkipDirList
176 SkipDirString
= '|'.join(SkipDirs
)
177 # p = re.compile(r'.*[\\/](?:%s)[\\/]?.*' % SkipDirString)
178 p
= re
.compile(r
'.*[\\/](?:%s^\S)[\\/]?.*' % SkipDirString
)
179 for scanFolder
in ScanFolders
:
180 for Root
, Dirs
, Files
in os
.walk(scanFolder
):
181 if p
.match(Root
.upper()):
184 Dirname
= os
.path
.join(Root
, Dir
)
185 if os
.path
.islink(Dirname
):
186 Dirname
= os
.path
.realpath(Dirname
)
187 if os
.path
.isdir(Dirname
):
188 # symlinks to directories are treated as directories
193 if len(File
) > 4 and File
[-4:].upper() == ".DEC":
194 Filename
= os
.path
.normpath(os
.path
.join(Root
, File
))
195 EdkLogger
.quiet("Parsing %s" % Filename
)
196 Op
.write("%s\r" % Filename
)
197 #Dec(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)
198 self
.MetaFile
= DecParser(Filename
, MODEL_FILE_DEC
, EccGlobalData
.gDb
.TblDec
)
199 self
.MetaFile
.Start()
201 if len(File
) > 4 and File
[-4:].upper() == ".DSC":
202 Filename
= os
.path
.normpath(os
.path
.join(Root
, File
))
203 EdkLogger
.quiet("Parsing %s" % Filename
)
204 Op
.write("%s\r" % Filename
)
205 #Dsc(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)
206 self
.MetaFile
= DscParser(PathClass(Filename
, Root
), MODEL_FILE_DSC
, MetaFileStorage(EccGlobalData
.gDb
.TblDsc
.Cur
, Filename
, MODEL_FILE_DSC
, True))
207 # always do post-process, in case of macros change
208 self
.MetaFile
.DoPostProcess()
209 self
.MetaFile
.Start()
210 self
.MetaFile
._PostProcess
()
212 if len(File
) > 4 and File
[-4:].upper() == ".INF":
213 Filename
= os
.path
.normpath(os
.path
.join(Root
, File
))
214 EdkLogger
.quiet("Parsing %s" % Filename
)
215 Op
.write("%s\r" % Filename
)
216 #Inf(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)
217 self
.MetaFile
= InfParser(Filename
, MODEL_FILE_INF
, EccGlobalData
.gDb
.TblInf
)
218 self
.MetaFile
.Start()
220 if len(File
) > 4 and File
[-4:].upper() == ".FDF":
221 Filename
= os
.path
.normpath(os
.path
.join(Root
, File
))
222 EdkLogger
.quiet("Parsing %s" % Filename
)
223 Op
.write("%s\r" % Filename
)
224 Fdf(Filename
, True, EccGlobalData
.gWorkspace
, EccGlobalData
.gDb
)
226 if len(File
) > 4 and File
[-4:].upper() == ".UNI":
227 Filename
= os
.path
.normpath(os
.path
.join(Root
, File
))
228 EdkLogger
.quiet("Parsing %s" % Filename
)
229 Op
.write("%s\r" % Filename
)
230 FileID
= EccGlobalData
.gDb
.TblFile
.InsertFile(Filename
, MODEL_FILE_UNI
)
231 EccGlobalData
.gDb
.TblReport
.UpdateBelongsToItemByFile(FileID
, File
)
237 EccGlobalData
.gDb
.Conn
.commit()
239 EdkLogger
.quiet("Building database for meta data files done!")
243 # Check each checkpoint
246 EdkLogger
.quiet("Checking ...")
249 EdkLogger
.quiet("Checking done!")
253 # Generate the scan report
256 EdkLogger
.quiet("Generating report ...")
257 EccGlobalData
.gDb
.TblReport
.ToCSV(self
.ReportFile
)
258 EdkLogger
.quiet("Generating report done!")
260 def GetRealPathCase(self
, path
):
261 TmpPath
= path
.rstrip(os
.sep
)
262 PathParts
= TmpPath
.split(os
.sep
)
263 if len(PathParts
) == 0:
265 if len(PathParts
) == 1:
266 if PathParts
[0].strip().endswith(':'):
267 return PathParts
[0].upper()
268 # Relative dir, list . current dir
269 Dirs
= os
.listdir('.')
271 if Dir
.upper() == PathParts
[0].upper():
274 if PathParts
[0].strip().endswith(':'):
275 PathParts
[0] = PathParts
[0].upper()
276 ParentDir
= PathParts
[0]
278 if PathParts
[0] == '':
282 PathParts
.remove(PathParts
[0]) # need to remove the parent
283 for Part
in PathParts
:
284 Dirs
= os
.listdir(ParentDir
+ os
.sep
)
286 if Dir
.upper() == Part
.upper():
299 def ParseOption(self
):
300 (Options
, Target
) = self
.EccOptionParser()
302 if Options
.Workspace
:
303 os
.environ
["WORKSPACE"] = Options
.Workspace
305 # Check workspace environment
306 if "WORKSPACE" not in os
.environ
:
307 EdkLogger
.error("ECC", BuildToolError
.ATTRIBUTE_NOT_AVAILABLE
, "Environment variable not found",
308 ExtraData
="WORKSPACE")
310 EccGlobalData
.gWorkspace
= os
.path
.normpath(os
.getenv("WORKSPACE"))
311 if not os
.path
.exists(EccGlobalData
.gWorkspace
):
312 EdkLogger
.error("ECC", BuildToolError
.FILE_NOT_FOUND
, ExtraData
="WORKSPACE = %s" % EccGlobalData
.gWorkspace
)
313 os
.environ
["WORKSPACE"] = EccGlobalData
.gWorkspace
315 self
.SetLogLevel(Options
)
318 if Options
.ConfigFile
is not None:
319 self
.ConfigFile
= Options
.ConfigFile
320 if Options
.OutputFile
is not None:
321 self
.OutputFile
= Options
.OutputFile
322 if Options
.ReportFile
is not None:
323 self
.ReportFile
= Options
.ReportFile
324 if Options
.ExceptionFile
is not None:
325 self
.ExceptionFile
= Options
.ExceptionFile
326 if Options
.Target
is not None:
327 if not os
.path
.isdir(Options
.Target
):
328 EdkLogger
.error("ECC", BuildToolError
.OPTION_VALUE_INVALID
, ExtraData
="Target [%s] does NOT exist" % Options
.Target
)
330 EccGlobalData
.gTarget
= self
.GetRealPathCase(os
.path
.normpath(Options
.Target
))
332 EdkLogger
.warn("Ecc", EdkLogger
.ECC_ERROR
, "The target source tree was not specified, using current WORKSPACE instead!")
333 EccGlobalData
.gTarget
= os
.path
.normpath(os
.getenv("WORKSPACE"))
334 if Options
.keepdatabase
is not None:
336 if Options
.metadata
is not None and Options
.sourcecode
is not None:
337 EdkLogger
.error("ECC", BuildToolError
.OPTION_CONFLICT
, ExtraData
="-m and -s can't be specified at one time")
338 if Options
.metadata
is not None:
339 self
.ScanSourceCode
= False
340 if Options
.sourcecode
is not None:
341 self
.ScanMetaData
= False
342 if Options
.folders
is not None:
347 # Set current log level of the tool based on args
349 # @param Option: The option list including log level setting
351 def SetLogLevel(self
, Option
):
352 if Option
.verbose
is not None:
353 EdkLogger
.SetLevel(EdkLogger
.VERBOSE
)
354 elif Option
.quiet
is not None:
355 EdkLogger
.SetLevel(EdkLogger
.QUIET
)
356 elif Option
.debug
is not None:
357 EdkLogger
.SetLevel(Option
.debug
+ 1)
359 EdkLogger
.SetLevel(EdkLogger
.INFO
)
361 ## Parse command line options
363 # Using standard Python module optparse to parse command line option of this tool.
365 # @retval Opt A optparse.Values object containing the parsed options
366 # @retval Args Target of build command
368 def EccOptionParser(self
):
369 Parser
= OptionParser(description
= self
.Copyright
, version
= self
.Version
, prog
= "Ecc.exe", usage
= "%prog [options]")
370 Parser
.add_option("-t", "--target sourcepath", action
="store", type="string", dest
='Target',
371 help="Check all files under the target workspace.")
372 Parser
.add_option("-c", "--config filename", action
="store", type="string", dest
="ConfigFile",
373 help="Specify a configuration file. Defaultly use config.ini under ECC tool directory.")
374 Parser
.add_option("-o", "--outfile filename", action
="store", type="string", dest
="OutputFile",
375 help="Specify the name of an output file, if and only if one filename was specified.")
376 Parser
.add_option("-r", "--reportfile filename", action
="store", type="string", dest
="ReportFile",
377 help="Specify the name of an report file, if and only if one filename was specified.")
378 Parser
.add_option("-e", "--exceptionfile filename", action
="store", type="string", dest
="ExceptionFile",
379 help="Specify the name of an exception file, if and only if one filename was specified.")
380 Parser
.add_option("-m", "--metadata", action
="store_true", type=None, help="Only scan meta-data files information if this option is specified.")
381 Parser
.add_option("-s", "--sourcecode", action
="store_true", type=None, help="Only scan source code files information if this option is specified.")
382 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.")
383 Parser
.add_option("-l", "--log filename", action
="store", dest
="LogFile", help="""If specified, the tool should emit the changes that
384 were made by the tool after printing the result message.
385 If filename, the emit to the file, otherwise emit to
386 standard output. If no modifications were made, then do not
387 create a log file, or output a log message.""")
388 Parser
.add_option("-q", "--quiet", action
="store_true", type=None, help="Disable all messages except FATAL ERRORS.")
389 Parser
.add_option("-v", "--verbose", action
="store_true", type=None, help="Turn on verbose output with informational messages printed, "\
390 "including library instances selected, final dependency expression, "\
391 "and warning messages, etc.")
392 Parser
.add_option("-d", "--debug", action
="store", type="int", help="Enable debug messages at specified level.")
393 Parser
.add_option("-w", "--workspace", action
="store", type="string", dest
='Workspace', help="Specify workspace.")
394 Parser
.add_option("-f", "--folders", action
="store_true", type=None, help="Only scanning specified folders which are recorded in config.ini file.")
396 (Opt
, Args
)=Parser
.parse_args()
402 # This acts like the main() function for the script, unless it is 'import'ed into another
405 if __name__
== '__main__':
406 # Initialize log system
407 EdkLogger
.Initialize()
408 EdkLogger
.IsRaiseError
= False
410 StartTime
= time
.perf_counter()
412 FinishTime
= time
.perf_counter()
414 BuildDuration
= time
.strftime("%M:%S", time
.gmtime(int(round(FinishTime
- StartTime
))))
415 EdkLogger
.quiet("\n%s [%s]" % (time
.strftime("%H:%M:%S, %b.%d %Y", time
.localtime()), BuildDuration
))