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