]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Source/Python/Workspace/WorkspaceDatabase.py
BaseTools: Rename String to StringUtils.
[mirror_edk2.git] / BaseTools / Source / Python / Workspace / WorkspaceDatabase.py
CommitLineData
52302d4d
LG
1## @file\r
2# This file is used to create a database used by build tool\r
3#\r
b9a6d9d7 4# Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.<BR>\r
77177984 5# (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>\r
40d841f6 6# This program and the accompanying materials\r
52302d4d
LG
7# are licensed and made available under the terms and conditions of the BSD License\r
8# which accompanies this distribution. The full text of the license may be found at\r
9# http://opensource.org/licenses/bsd-license.php\r
10#\r
11# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13#\r
14\r
15##\r
16# Import Modules\r
17#\r
18import sqlite3\r
5a57246e 19from Common.StringUtils import *\r
52302d4d
LG
20from Common.DataType import *\r
21from Common.Misc import *\r
22from types import *\r
23\r
52302d4d
LG
24from MetaDataTable import *\r
25from MetaFileTable import *\r
26from MetaFileParser import *\r
52302d4d 27\r
ae7b6df8
LG
28from Workspace.DecBuildData import DecBuildData\r
29from Workspace.DscBuildData import DscBuildData\r
30from Workspace.InfBuildData import InfBuildData\r
52302d4d
LG
31\r
32## Database\r
33#\r
e56468c0 34# This class defined the build database for all modules, packages and platform.\r
52302d4d
LG
35# It will call corresponding parser for the given file if it cannot find it in\r
36# the database.\r
37#\r
38# @param DbPath Path of database file\r
39# @param GlobalMacros Global macros used for replacement during file parsing\r
40# @prarm RenewDb=False Create new database file if it's already there\r
41#\r
42class WorkspaceDatabase(object):\r
52302d4d 43\r
52302d4d
LG
44 #\r
45 # internal class used for call corresponding file parser and caching the result\r
46 # to avoid unnecessary re-parsing\r
47 #\r
48 class BuildObjectFactory(object):\r
0d2711a6 49\r
52302d4d
LG
50 _FILE_TYPE_ = {\r
51 ".inf" : MODEL_FILE_INF,\r
52 ".dec" : MODEL_FILE_DEC,\r
53 ".dsc" : MODEL_FILE_DSC,\r
0d2711a6
LG
54 }\r
55\r
56 # file parser\r
57 _FILE_PARSER_ = {\r
58 MODEL_FILE_INF : InfParser,\r
59 MODEL_FILE_DEC : DecParser,\r
60 MODEL_FILE_DSC : DscParser,\r
52302d4d
LG
61 }\r
62\r
63 # convert to xxxBuildData object\r
64 _GENERATOR_ = {\r
65 MODEL_FILE_INF : InfBuildData,\r
66 MODEL_FILE_DEC : DecBuildData,\r
67 MODEL_FILE_DSC : DscBuildData,\r
52302d4d
LG
68 }\r
69\r
70 _CACHE_ = {} # (FilePath, Arch) : <object>\r
71\r
72 # constructor\r
73 def __init__(self, WorkspaceDb):\r
74 self.WorkspaceDb = WorkspaceDb\r
75\r
0d2711a6 76 # key = (FilePath, Arch=None)\r
52302d4d
LG
77 def __contains__(self, Key):\r
78 FilePath = Key[0]\r
52302d4d
LG
79 if len(Key) > 1:\r
80 Arch = Key[1]\r
0d2711a6
LG
81 else:\r
82 Arch = None\r
52302d4d
LG
83 return (FilePath, Arch) in self._CACHE_\r
84\r
0d2711a6 85 # key = (FilePath, Arch=None, Target=None, Toochain=None)\r
52302d4d
LG
86 def __getitem__(self, Key):\r
87 FilePath = Key[0]\r
0d2711a6
LG
88 KeyLength = len(Key)\r
89 if KeyLength > 1:\r
52302d4d 90 Arch = Key[1]\r
0d2711a6
LG
91 else:\r
92 Arch = None\r
93 if KeyLength > 2:\r
94 Target = Key[2]\r
95 else:\r
96 Target = None\r
97 if KeyLength > 3:\r
98 Toolchain = Key[3]\r
99 else:\r
100 Toolchain = None\r
52302d4d
LG
101\r
102 # if it's generated before, just return the cached one\r
0d2711a6 103 Key = (FilePath, Arch, Target, Toolchain)\r
52302d4d
LG
104 if Key in self._CACHE_:\r
105 return self._CACHE_[Key]\r
106\r
107 # check file type\r
0d2711a6 108 Ext = FilePath.Type\r
52302d4d
LG
109 if Ext not in self._FILE_TYPE_:\r
110 return None\r
111 FileType = self._FILE_TYPE_[Ext]\r
112 if FileType not in self._GENERATOR_:\r
113 return None\r
114\r
0d2711a6
LG
115 # get the parser ready for this file\r
116 MetaFile = self._FILE_PARSER_[FileType](\r
117 FilePath, \r
118 FileType, \r
cdd1b5e5 119 Arch,\r
0d2711a6
LG
120 MetaFileStorage(self.WorkspaceDb.Cur, FilePath, FileType)\r
121 )\r
122 # alwasy do post-process, in case of macros change\r
123 MetaFile.DoPostProcess()\r
124 # object the build is based on\r
52302d4d
LG
125 BuildObject = self._GENERATOR_[FileType](\r
126 FilePath,\r
127 MetaFile,\r
128 self,\r
129 Arch,\r
0d2711a6
LG
130 Target,\r
131 Toolchain\r
52302d4d
LG
132 )\r
133 self._CACHE_[Key] = BuildObject\r
134 return BuildObject\r
135\r
136 # placeholder for file format conversion\r
137 class TransformObjectFactory:\r
138 def __init__(self, WorkspaceDb):\r
139 self.WorkspaceDb = WorkspaceDb\r
140\r
141 # key = FilePath, Arch\r
142 def __getitem__(self, Key):\r
143 pass\r
144\r
145 ## Constructor of WorkspaceDatabase\r
146 #\r
147 # @param DbPath Path of database file\r
148 # @param GlobalMacros Global macros used for replacement during file parsing\r
149 # @prarm RenewDb=False Create new database file if it's already there\r
150 #\r
0d2711a6 151 def __init__(self, DbPath, RenewDb=False):\r
4234283c 152 self._DbClosedFlag = False\r
0d2711a6 153 if not DbPath:\r
05cc51ad 154 DbPath = os.path.normpath(mws.join(GlobalData.gWorkspace, 'Conf', GlobalData.gDatabasePath))\r
52302d4d
LG
155\r
156 # don't create necessary path for db in memory\r
157 if DbPath != ':memory:':\r
158 DbDir = os.path.split(DbPath)[0]\r
159 if not os.path.exists(DbDir):\r
160 os.makedirs(DbDir)\r
161\r
162 # remove db file in case inconsistency between db and file in file system\r
163 if self._CheckWhetherDbNeedRenew(RenewDb, DbPath):\r
164 os.remove(DbPath)\r
165 \r
166 # create db with optimized parameters\r
167 self.Conn = sqlite3.connect(DbPath, isolation_level='DEFERRED')\r
168 self.Conn.execute("PRAGMA synchronous=OFF")\r
169 self.Conn.execute("PRAGMA temp_store=MEMORY")\r
170 self.Conn.execute("PRAGMA count_changes=OFF")\r
171 self.Conn.execute("PRAGMA cache_size=8192")\r
172 #self.Conn.execute("PRAGMA page_size=8192")\r
173\r
174 # to avoid non-ascii character conversion issue\r
175 self.Conn.text_factory = str\r
176 self.Cur = self.Conn.cursor()\r
177\r
178 # create table for internal uses\r
179 self.TblDataModel = TableDataModel(self.Cur)\r
180 self.TblFile = TableFile(self.Cur)\r
0d2711a6 181 self.Platform = None\r
52302d4d
LG
182\r
183 # conversion object for build or file format conversion purpose\r
184 self.BuildObject = WorkspaceDatabase.BuildObjectFactory(self)\r
185 self.TransformObject = WorkspaceDatabase.TransformObjectFactory(self)\r
186\r
187 ## Check whether workspace database need to be renew.\r
188 # The renew reason maybe:\r
189 # 1) If user force to renew;\r
190 # 2) If user do not force renew, and\r
191 # a) If the time of last modified python source is newer than database file;\r
192 # b) If the time of last modified frozen executable file is newer than database file;\r
193 #\r
194 # @param force User force renew database\r
195 # @param DbPath The absolute path of workspace database file\r
196 #\r
197 # @return Bool value for whether need renew workspace databse\r
198 #\r
199 def _CheckWhetherDbNeedRenew (self, force, DbPath):\r
52302d4d
LG
200 # if database does not exist, we need do nothing\r
201 if not os.path.exists(DbPath): return False\r
202 \r
203 # if user force to renew database, then not check whether database is out of date\r
204 if force: return True\r
205 \r
206 # \r
207 # Check the time of last modified source file or build.exe\r
208 # if is newer than time of database, then database need to be re-created.\r
209 #\r
210 timeOfToolModified = 0\r
211 if hasattr(sys, "frozen"):\r
212 exePath = os.path.abspath(sys.executable)\r
213 timeOfToolModified = os.stat(exePath).st_mtime\r
214 else:\r
215 curPath = os.path.dirname(__file__) # curPath is the path of WorkspaceDatabase.py\r
216 rootPath = os.path.split(curPath)[0] # rootPath is root path of python source, such as /BaseTools/Source/Python\r
4231a819 217 if rootPath == "" or rootPath is None:\r
52302d4d
LG
218 EdkLogger.verbose("\nFail to find the root path of build.exe or python sources, so can not \\r
219determine whether database file is out of date!\n")\r
220 \r
221 # walk the root path of source or build's binary to get the time last modified.\r
222 \r
223 for root, dirs, files in os.walk (rootPath):\r
224 for dir in dirs:\r
225 # bypass source control folder \r
226 if dir.lower() in [".svn", "_svn", "cvs"]:\r
227 dirs.remove(dir)\r
228 \r
229 for file in files:\r
230 ext = os.path.splitext(file)[1]\r
231 if ext.lower() == ".py": # only check .py files\r
232 fd = os.stat(os.path.join(root, file))\r
233 if timeOfToolModified < fd.st_mtime:\r
234 timeOfToolModified = fd.st_mtime\r
235 if timeOfToolModified > os.stat(DbPath).st_mtime:\r
236 EdkLogger.verbose("\nWorkspace database is out of data!")\r
237 return True\r
238 \r
239 return False\r
240 \r
241 ## Initialize build database\r
242 def InitDatabase(self):\r
243 EdkLogger.verbose("\nInitialize build database started ...")\r
244\r
245 #\r
246 # Create new tables\r
247 #\r
248 self.TblDataModel.Create(False)\r
249 self.TblFile.Create(False)\r
250\r
251 #\r
252 # Initialize table DataModel\r
253 #\r
254 self.TblDataModel.InitTable()\r
255 EdkLogger.verbose("Initialize build database ... DONE!")\r
256\r
257 ## Query a table\r
258 #\r
259 # @param Table: The instance of the table to be queried\r
260 #\r
261 def QueryTable(self, Table):\r
262 Table.Query()\r
263\r
0d2711a6
LG
264 def __del__(self):\r
265 self.Close()\r
266\r
52302d4d
LG
267 ## Close entire database\r
268 #\r
269 # Commit all first\r
270 # Close the connection and cursor\r
271 #\r
272 def Close(self):\r
4234283c
LG
273 if not self._DbClosedFlag:\r
274 self.Conn.commit()\r
275 self.Cur.close()\r
276 self.Conn.close()\r
277 self._DbClosedFlag = True\r
52302d4d 278\r
52302d4d 279 ## Summarize all packages in the database\r
0d2711a6
LG
280 def GetPackageList(self, Platform, Arch, TargetName, ToolChainTag):\r
281 self.Platform = Platform\r
47fea6af 282 PackageList = []\r
b9a6d9d7 283 Pa = self.BuildObject[self.Platform, Arch, TargetName, ToolChainTag]\r
0d2711a6
LG
284 #\r
285 # Get Package related to Modules\r
286 #\r
287 for Module in Pa.Modules:\r
288 ModuleObj = self.BuildObject[Module, Arch, TargetName, ToolChainTag]\r
289 for Package in ModuleObj.Packages:\r
52302d4d
LG
290 if Package not in PackageList:\r
291 PackageList.append(Package)\r
0d2711a6
LG
292 #\r
293 # Get Packages related to Libraries\r
294 #\r
295 for Lib in Pa.LibraryInstances:\r
296 LibObj = self.BuildObject[Lib, Arch, TargetName, ToolChainTag]\r
297 for Package in LibObj.Packages:\r
298 if Package not in PackageList:\r
47fea6af
YZ
299 PackageList.append(Package)\r
300\r
52302d4d
LG
301 return PackageList\r
302\r
303 ## Summarize all platforms in the database\r
304 def _GetPlatformList(self):\r
305 PlatformList = []\r
306 for PlatformFile in self.TblFile.GetFileList(MODEL_FILE_DSC):\r
307 try:\r
55c84777 308 Platform = self.BuildObject[PathClass(PlatformFile), TAB_COMMON]\r
52302d4d
LG
309 except:\r
310 Platform = None\r
4231a819 311 if Platform is not None:\r
52302d4d
LG
312 PlatformList.append(Platform)\r
313 return PlatformList\r
314\r
f0dc69e6 315 def _MapPlatform(self, Dscfile):\r
55c84777 316 Platform = self.BuildObject[PathClass(Dscfile), TAB_COMMON]\r
4231a819 317 if Platform is None:\r
d429fcd0 318 EdkLogger.error('build', PARSER_ERROR, "Failed to parser DSC file: %s" % Dscfile)\r
f0dc69e6
YZ
319 return Platform\r
320\r
52302d4d 321 PlatformList = property(_GetPlatformList)\r
52302d4d
LG
322\r
323##\r
324#\r
325# This acts like the main() function for the script, unless it is 'import'ed into another\r
326# script.\r
327#\r
328if __name__ == '__main__':\r
329 pass\r
330\r