## @file | |
# This file is used to create a database used by build tool | |
# | |
# Copyright (c) 2008 - 2017, Intel Corporation. All rights reserved.<BR> | |
# (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> | |
# This program and the accompanying materials | |
# are licensed and made available under the terms and conditions of the BSD License | |
# which accompanies this distribution. The full text of the license may be found at | |
# http://opensource.org/licenses/bsd-license.php | |
# | |
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
# | |
## | |
# Import Modules | |
# | |
import sqlite3 | |
from Common.String import * | |
from Common.DataType import * | |
from Common.Misc import * | |
from types import * | |
from MetaDataTable import * | |
from MetaFileTable import * | |
from MetaFileParser import * | |
from Workspace.DecBuildData import DecBuildData | |
from Workspace.DscBuildData import DscBuildData | |
from Workspace.InfBuildData import InfBuildData | |
## Database | |
# | |
# This class defined the build database for all modules, packages and platform. | |
# It will call corresponding parser for the given file if it cannot find it in | |
# the database. | |
# | |
# @param DbPath Path of database file | |
# @param GlobalMacros Global macros used for replacement during file parsing | |
# @prarm RenewDb=False Create new database file if it's already there | |
# | |
class WorkspaceDatabase(object): | |
# | |
# internal class used for call corresponding file parser and caching the result | |
# to avoid unnecessary re-parsing | |
# | |
class BuildObjectFactory(object): | |
_FILE_TYPE_ = { | |
".inf" : MODEL_FILE_INF, | |
".dec" : MODEL_FILE_DEC, | |
".dsc" : MODEL_FILE_DSC, | |
} | |
# file parser | |
_FILE_PARSER_ = { | |
MODEL_FILE_INF : InfParser, | |
MODEL_FILE_DEC : DecParser, | |
MODEL_FILE_DSC : DscParser, | |
} | |
# convert to xxxBuildData object | |
_GENERATOR_ = { | |
MODEL_FILE_INF : InfBuildData, | |
MODEL_FILE_DEC : DecBuildData, | |
MODEL_FILE_DSC : DscBuildData, | |
} | |
_CACHE_ = {} # (FilePath, Arch) : <object> | |
# constructor | |
def __init__(self, WorkspaceDb): | |
self.WorkspaceDb = WorkspaceDb | |
# key = (FilePath, Arch=None) | |
def __contains__(self, Key): | |
FilePath = Key[0] | |
if len(Key) > 1: | |
Arch = Key[1] | |
else: | |
Arch = None | |
return (FilePath, Arch) in self._CACHE_ | |
# key = (FilePath, Arch=None, Target=None, Toochain=None) | |
def __getitem__(self, Key): | |
FilePath = Key[0] | |
KeyLength = len(Key) | |
if KeyLength > 1: | |
Arch = Key[1] | |
else: | |
Arch = None | |
if KeyLength > 2: | |
Target = Key[2] | |
else: | |
Target = None | |
if KeyLength > 3: | |
Toolchain = Key[3] | |
else: | |
Toolchain = None | |
# if it's generated before, just return the cached one | |
Key = (FilePath, Arch, Target, Toolchain) | |
if Key in self._CACHE_: | |
return self._CACHE_[Key] | |
# check file type | |
Ext = FilePath.Type | |
if Ext not in self._FILE_TYPE_: | |
return None | |
FileType = self._FILE_TYPE_[Ext] | |
if FileType not in self._GENERATOR_: | |
return None | |
# get the parser ready for this file | |
MetaFile = self._FILE_PARSER_[FileType]( | |
FilePath, | |
FileType, | |
Arch, | |
MetaFileStorage(self.WorkspaceDb.Cur, FilePath, FileType) | |
) | |
# alwasy do post-process, in case of macros change | |
MetaFile.DoPostProcess() | |
# object the build is based on | |
BuildObject = self._GENERATOR_[FileType]( | |
FilePath, | |
MetaFile, | |
self, | |
Arch, | |
Target, | |
Toolchain | |
) | |
self._CACHE_[Key] = BuildObject | |
return BuildObject | |
# placeholder for file format conversion | |
class TransformObjectFactory: | |
def __init__(self, WorkspaceDb): | |
self.WorkspaceDb = WorkspaceDb | |
# key = FilePath, Arch | |
def __getitem__(self, Key): | |
pass | |
## Constructor of WorkspaceDatabase | |
# | |
# @param DbPath Path of database file | |
# @param GlobalMacros Global macros used for replacement during file parsing | |
# @prarm RenewDb=False Create new database file if it's already there | |
# | |
def __init__(self, DbPath, RenewDb=False): | |
self._DbClosedFlag = False | |
if not DbPath: | |
DbPath = os.path.normpath(mws.join(GlobalData.gWorkspace, 'Conf', GlobalData.gDatabasePath)) | |
# don't create necessary path for db in memory | |
if DbPath != ':memory:': | |
DbDir = os.path.split(DbPath)[0] | |
if not os.path.exists(DbDir): | |
os.makedirs(DbDir) | |
# remove db file in case inconsistency between db and file in file system | |
if self._CheckWhetherDbNeedRenew(RenewDb, DbPath): | |
os.remove(DbPath) | |
# create db with optimized parameters | |
self.Conn = sqlite3.connect(DbPath, isolation_level='DEFERRED') | |
self.Conn.execute("PRAGMA synchronous=OFF") | |
self.Conn.execute("PRAGMA temp_store=MEMORY") | |
self.Conn.execute("PRAGMA count_changes=OFF") | |
self.Conn.execute("PRAGMA cache_size=8192") | |
#self.Conn.execute("PRAGMA page_size=8192") | |
# to avoid non-ascii character conversion issue | |
self.Conn.text_factory = str | |
self.Cur = self.Conn.cursor() | |
# create table for internal uses | |
self.TblDataModel = TableDataModel(self.Cur) | |
self.TblFile = TableFile(self.Cur) | |
self.Platform = None | |
# conversion object for build or file format conversion purpose | |
self.BuildObject = WorkspaceDatabase.BuildObjectFactory(self) | |
self.TransformObject = WorkspaceDatabase.TransformObjectFactory(self) | |
## Check whether workspace database need to be renew. | |
# The renew reason maybe: | |
# 1) If user force to renew; | |
# 2) If user do not force renew, and | |
# a) If the time of last modified python source is newer than database file; | |
# b) If the time of last modified frozen executable file is newer than database file; | |
# | |
# @param force User force renew database | |
# @param DbPath The absolute path of workspace database file | |
# | |
# @return Bool value for whether need renew workspace databse | |
# | |
def _CheckWhetherDbNeedRenew (self, force, DbPath): | |
# if database does not exist, we need do nothing | |
if not os.path.exists(DbPath): return False | |
# if user force to renew database, then not check whether database is out of date | |
if force: return True | |
# | |
# Check the time of last modified source file or build.exe | |
# if is newer than time of database, then database need to be re-created. | |
# | |
timeOfToolModified = 0 | |
if hasattr(sys, "frozen"): | |
exePath = os.path.abspath(sys.executable) | |
timeOfToolModified = os.stat(exePath).st_mtime | |
else: | |
curPath = os.path.dirname(__file__) # curPath is the path of WorkspaceDatabase.py | |
rootPath = os.path.split(curPath)[0] # rootPath is root path of python source, such as /BaseTools/Source/Python | |
if rootPath == "" or rootPath == None: | |
EdkLogger.verbose("\nFail to find the root path of build.exe or python sources, so can not \ | |
determine whether database file is out of date!\n") | |
# walk the root path of source or build's binary to get the time last modified. | |
for root, dirs, files in os.walk (rootPath): | |
for dir in dirs: | |
# bypass source control folder | |
if dir.lower() in [".svn", "_svn", "cvs"]: | |
dirs.remove(dir) | |
for file in files: | |
ext = os.path.splitext(file)[1] | |
if ext.lower() == ".py": # only check .py files | |
fd = os.stat(os.path.join(root, file)) | |
if timeOfToolModified < fd.st_mtime: | |
timeOfToolModified = fd.st_mtime | |
if timeOfToolModified > os.stat(DbPath).st_mtime: | |
EdkLogger.verbose("\nWorkspace database is out of data!") | |
return True | |
return False | |
## Initialize build database | |
def InitDatabase(self): | |
EdkLogger.verbose("\nInitialize build database started ...") | |
# | |
# Create new tables | |
# | |
self.TblDataModel.Create(False) | |
self.TblFile.Create(False) | |
# | |
# Initialize table DataModel | |
# | |
self.TblDataModel.InitTable() | |
EdkLogger.verbose("Initialize build database ... DONE!") | |
## Query a table | |
# | |
# @param Table: The instance of the table to be queried | |
# | |
def QueryTable(self, Table): | |
Table.Query() | |
def __del__(self): | |
self.Close() | |
## Close entire database | |
# | |
# Commit all first | |
# Close the connection and cursor | |
# | |
def Close(self): | |
if not self._DbClosedFlag: | |
self.Conn.commit() | |
self.Cur.close() | |
self.Conn.close() | |
self._DbClosedFlag = True | |
## Summarize all packages in the database | |
def GetPackageList(self, Platform, Arch, TargetName, ToolChainTag): | |
self.Platform = Platform | |
PackageList = [] | |
Pa = self.BuildObject[self.Platform, 'COMMON'] | |
# | |
# Get Package related to Modules | |
# | |
for Module in Pa.Modules: | |
ModuleObj = self.BuildObject[Module, Arch, TargetName, ToolChainTag] | |
for Package in ModuleObj.Packages: | |
if Package not in PackageList: | |
PackageList.append(Package) | |
# | |
# Get Packages related to Libraries | |
# | |
for Lib in Pa.LibraryInstances: | |
LibObj = self.BuildObject[Lib, Arch, TargetName, ToolChainTag] | |
for Package in LibObj.Packages: | |
if Package not in PackageList: | |
PackageList.append(Package) | |
return PackageList | |
## Summarize all platforms in the database | |
def _GetPlatformList(self): | |
PlatformList = [] | |
for PlatformFile in self.TblFile.GetFileList(MODEL_FILE_DSC): | |
try: | |
Platform = self.BuildObject[PathClass(PlatformFile), 'COMMON'] | |
except: | |
Platform = None | |
if Platform != None: | |
PlatformList.append(Platform) | |
return PlatformList | |
def _MapPlatform(self, Dscfile): | |
Platform = self.BuildObject[PathClass(Dscfile), 'COMMON'] | |
if Platform == None: | |
EdkLogger.error('build', PARSER_ERROR, "Failed to parser DSC file: %s" % Dscfile) | |
return Platform | |
PlatformList = property(_GetPlatformList) | |
## | |
# | |
# This acts like the main() function for the script, unless it is 'import'ed into another | |
# script. | |
# | |
if __name__ == '__main__': | |
pass | |