]>
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 | |
18 | import sqlite3\r | |
5a57246e | 19 | from Common.StringUtils import *\r |
52302d4d LG |
20 | from Common.DataType import *\r |
21 | from Common.Misc import *\r | |
22 | from types import *\r | |
23 | \r | |
52302d4d LG |
24 | from MetaDataTable import *\r |
25 | from MetaFileTable import *\r | |
26 | from MetaFileParser import *\r | |
52302d4d | 27 | \r |
ae7b6df8 LG |
28 | from Workspace.DecBuildData import DecBuildData\r |
29 | from Workspace.DscBuildData import DscBuildData\r | |
30 | from 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 | |
42 | class 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 |
219 | determine 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 | |
328 | if __name__ == '__main__':\r | |
329 | pass\r | |
330 | \r |