For case-insensitive file systems, edk2 String.py collides with the Python string.py, which results in build errors. This,for example, applies to building via the Windows Subsystem for Linux from a DriveFS file system. This patch renames String to StringUtils to prevent conflicts for case-insensitive file systems. Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Marvin Haeuser <Marvin.Haeuser@outlook.com> Reviewed-by: Liming Gao <liming.gao@intel.com>
		
			
				
	
	
		
			331 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			331 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
## @file
 | 
						|
# This file is used to create a database used by build tool
 | 
						|
#
 | 
						|
# Copyright (c) 2008 - 2018, 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.StringUtils 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 is 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, Arch, TargetName, ToolChainTag]
 | 
						|
        #
 | 
						|
        # 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), TAB_COMMON]
 | 
						|
            except:
 | 
						|
                Platform = None
 | 
						|
            if Platform is not None:
 | 
						|
                PlatformList.append(Platform)
 | 
						|
        return PlatformList
 | 
						|
 | 
						|
    def _MapPlatform(self, Dscfile):
 | 
						|
        Platform = self.BuildObject[PathClass(Dscfile), TAB_COMMON]
 | 
						|
        if Platform is 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
 | 
						|
 |