4040421a |
1 | #!/usr/bin/env python |
2 | |
3 | """This is a python script that takes user input from the command line and |
4 | installs a far (Framework Archive Manifest) file into the workspace.""" |
5 | |
6 | import os, sys, getopt, string, xml.dom.minidom, zipfile, md5 |
7 | from XmlRoutines import * |
8 | from WorkspaceRoutines import * |
9 | |
312ffece |
10 | class Flags: |
11 | """Keep track of some command line flags and operating modes.""" |
12 | def __init__(self): |
13 | self.verbose = False |
14 | self.force = False |
15 | self.reinstall = False |
822d4f3a |
16 | self.dir = '' |
4040421a |
17 | |
18 | class Database: |
19 | |
312ffece |
20 | """This class encapsulates the FrameworkDatabase file for the workspace we |
21 | are operating on.""" |
22 | |
4040421a |
23 | def __init__(self, filename="Tools/Conf/FrameworkDatabase.db"): |
24 | |
25 | # First try to get a lock file. |
26 | self.DBFile = inWorkspace(filename) |
27 | self.lockfile = inWorkspace("Tools/Conf/FrameworkDatabase.lock") |
28 | if os.path.exists(self.lockfile): |
29 | self.itsMyLockFile = False |
30 | print "Error: The database file is locked by ", self.lockfile |
31 | raise OSError("The Database is locked.") |
32 | else: |
33 | self.lock = open(self.lockfile, 'w') |
34 | self.lock.write("pid "+str(os.getpid())) |
35 | self.itsMyLockFile = True |
36 | |
37 | self.dom = XmlParseFile(inWorkspace(filename)) |
38 | |
39 | self.installedPackages = {} |
40 | self.installedPlatforms = {} |
41 | self.installedFars = {} |
42 | |
43 | for spdfile in XmlList(self.dom, "/FrameworkDatabase/PackageList/Filename"): |
44 | filename = str(XmlElementData(spdfile)) |
45 | spd = XmlParseFileSection(inWorkspace(filename), "SpdHeader") |
312ffece |
46 | self.installedPackages[GetSpdGuidVersion(spd, 1)] = \ |
4040421a |
47 | XmlElement(spd, "/SpdHeader/PackageName") |
48 | |
49 | for fpdfile in XmlList(self.dom, "/FrameworkDatabase/PlatformList/Filename"): |
50 | filename = str(XmlElementData(fpdfile)) |
51 | fpd = XmlParseFileSection(inWorkspace(filename), "PlatformHeader") |
312ffece |
52 | self.installedPlatforms[GetFpdGuidVersion(fpd, 1)] = \ |
4040421a |
53 | XmlElement(fpd, "/PlatformHeader/PlatformName") |
54 | |
55 | for farfile in XmlList(self.dom, "/FrameworkDatabase/FarList/Filename"): |
822d4f3a |
56 | farGuid = Guid(farfile.getAttribute("FarGuid")) |
4040421a |
57 | self.installedFars[farGuid] = XmlElementData(farfile) |
58 | |
59 | self.packageList = XmlNode(self.dom, "/FrameworkDatabase/PackageList") |
60 | self.platformList = XmlNode(self.dom, "/FrameworkDatabase/PlatformList") |
61 | self.farList = XmlNode(self.dom, "/FrameworkDatabase/FarList") |
62 | |
63 | def __del__(self): |
64 | if self.itsMyLockFile: |
65 | self.lock.close() |
66 | os.unlink(self.lockfile) |
67 | |
24a86f9a |
68 | def HasPackage(self, (guid, version)): |
4040421a |
69 | """Return true iff this package is already installed.""" |
24a86f9a |
70 | if version == "": |
71 | # Look for the guid. |
72 | for (g, v) in self.installedPackages.keys(): |
73 | if g == guid: |
74 | return True |
4040421a |
75 | return self.installedPackages.has_key((guid, version)) |
76 | |
24a86f9a |
77 | def HasPlatform(self, (guid, version)): |
4040421a |
78 | """Return true iff this platform is already installed.""" |
24a86f9a |
79 | if version == "": |
80 | # Look for the guid. |
81 | for (g, v) in self.installedPlatforms.keys(): |
82 | if g == guid: |
83 | return True |
4040421a |
84 | return self.installedPlatforms.has_key((guid, version)) |
85 | |
86 | def HasFar(self, farguid): |
87 | """Return true iff this far is already installed.""" |
88 | return self.installedFars.has_key(farguid) |
89 | |
90 | def AddPackage(self, f): |
312ffece |
91 | """Put this package in the database""" |
92 | XmlAppendChildElement(self.packageList, "Filename", f) |
4040421a |
93 | |
94 | def AddPlatform(self, f): |
312ffece |
95 | """Put this platform in the database""" |
96 | XmlAppendChildElement(self.platformList, "Filename", f) |
4040421a |
97 | |
98 | def AddFar(self, f, guid=""): |
312ffece |
99 | """Put this far in the database""" |
100 | XmlAppendChildElement(self.farList, "Filename", f, {"FarGuid":guid} ) |
4040421a |
101 | |
102 | def Write(self): |
312ffece |
103 | """Save the Xml tree out to the file.""" |
4040421a |
104 | if True: |
105 | XmlSaveFile(self.dom, self.DBFile) |
106 | else: |
107 | f=open(self.DBFile, 'w') |
108 | f.write(self.dom.toprettyxml(2*" ")) |
109 | f.close() |
110 | |
312ffece |
111 | def ExtractFile(zip, file, defaultDir="", workspaceLocation="", md5sum=""): |
112 | """Unzip a file.""" |
113 | if flags.verbose: |
4040421a |
114 | print "Extracting ", file |
115 | |
116 | destFile = os.path.join(inWorkspace(workspaceLocation), str(file)) |
117 | destDir = os.path.dirname(destFile) |
118 | |
119 | mkdir(destDir) |
120 | |
121 | f = open(destFile, "w") |
312ffece |
122 | contents = zip.read(os.path.join(defaultDir,file)) |
123 | if md5sum and (md5.md5(contents).hexdigest() != md5sum): |
124 | print "Error: The md5 sum does not match on file %s." % file |
125 | f.write(contents) |
4040421a |
126 | f.close() |
127 | |
312ffece |
128 | def GetFpdGuidVersion(Dom, strip=0): |
24a86f9a |
129 | |
130 | """Get the Guid and version of the fpd from a dom object.""" |
131 | |
312ffece |
132 | gpath = ["PlatformSurfaceArea", "PlatformHeader", "GuidValue"] |
133 | vpath = ["PlatformSurfaceArea", "PlatformHeader", "Version"] |
24a86f9a |
134 | |
822d4f3a |
135 | return Guid(XmlElement(Dom, "/".join(gpath[strip:]))), \ |
136 | XmlElement(Dom, "/".join(vpath[strip:])) |
312ffece |
137 | |
138 | def GetSpdGuidVersion(Dom, strip=0): |
24a86f9a |
139 | |
140 | """Get the Guid and version of the spd from a dom object.""" |
141 | |
312ffece |
142 | gpath = ["PackageSurfaceArea", "SpdHeader", "GuidValue"] |
143 | vpath = ["PackageSurfaceArea", "SpdHeader", "Version"] |
144 | |
822d4f3a |
145 | return Guid(XmlElement(Dom, "/".join(gpath[strip:]))), \ |
146 | XmlElement(Dom, "/".join(vpath[strip:])) |
24a86f9a |
147 | |
4040421a |
148 | def InstallFar(farfile, workspaceLocation=""): |
149 | |
312ffece |
150 | """Unpack the far an install it in the workspace. We need to adhere to the |
151 | rules of far handling.""" |
152 | |
4040421a |
153 | far = zipfile.ZipFile(farfile, "r") |
154 | |
155 | # Use this list to make sure we get everything from the far. |
156 | zipContents = far.namelist() |
157 | |
158 | manifest = xml.dom.minidom.parseString(far.read("FrameworkArchiveManifest.xml")) |
159 | zipContents.remove("FrameworkArchiveManifest.xml") |
160 | fdb = Database() |
161 | |
162 | # First we need to make sure that the far will install cleanly. |
163 | |
24a86f9a |
164 | installError = False # Let's hope for the best. |
165 | spdDoms = [] |
166 | farSpds = [] |
167 | |
4040421a |
168 | # Check the packages |
169 | for farPackage in XmlList(manifest, "/FrameworkArchiveManifest/FarPackageList/FarPackage/FarFilename"): |
170 | spdfile = str(XmlElementData(farPackage)) |
312ffece |
171 | spd = XmlParseStringSection(far.read(spdfile), "SpdHeader") |
172 | packageGV = GetSpdGuidVersion(spd, 1) |
24a86f9a |
173 | if fdb.HasPackage(packageGV): |
312ffece |
174 | if not flags.reinstall: |
175 | print "Error: This package is already installed: ", spdfile |
176 | installError = True |
4040421a |
177 | |
24a86f9a |
178 | # Build up a list of the package guid versions that this far is bringing in. |
179 | # This is needed to satisfy dependencies of msas that are in the other packages of |
180 | # this far. |
24a86f9a |
181 | farSpds.append(packageGV) |
182 | |
312ffece |
183 | spdDoms.append((spd, spdfile)) |
24a86f9a |
184 | |
312ffece |
185 | for spd, spdfile in spdDoms: |
24a86f9a |
186 | # Now we need to get a list of every msa in this spd and check the package dependencies. |
187 | for msafile in XmlList(spd, "/PackageSurfaceArea/MsaFiles/Filename"): |
188 | msafilePath = str(os.path.join(os.path.dirname(spdfile), XmlElementData(msafile))) |
189 | |
190 | msa = XmlParseString(far.read(msafilePath)) |
191 | |
192 | for package in XmlList(msa, "/ModuleSurfaceArea/PackageDependencies/Package"): |
822d4f3a |
193 | guid = Guid(package.getAttribute("PackageGuid")) |
24a86f9a |
194 | version = package.getAttribute("PackageVersion") |
195 | |
312ffece |
196 | # Does anyone provide this package? |
24a86f9a |
197 | if not fdb.HasPackage((guid, version)) and not (guid, version) in farSpds: |
312ffece |
198 | print ("Error: The module %s depends on the package guid %s version %s, which " + \ |
199 | "is not installed in the workspace, nor is it provided by this far.") \ |
200 | % (msafilePath, guid, version) |
24a86f9a |
201 | installError = True |
202 | |
4040421a |
203 | # Check the platforms |
204 | for farPlatform in XmlList(manifest, "/FrameworkArchiveManifest/FarPlatformList/FarPlatform/FarFilename"): |
205 | fpdfile = str(XmlElementData(farPlatform)) |
24a86f9a |
206 | fpd = XmlParseString(far.read(fpdfile)) |
312ffece |
207 | if fdb.HasPlatform(GetFpdGuidVersion(fpd, 0)): |
208 | if not flags.reinstall: |
209 | print "Error: This platform is already installed: ", fpdfile |
210 | installError = True |
211 | |
212 | # Now we need to check that all the Platforms (and modules?) that are |
213 | # referenced by this fpd are installed in the workspace or are in this far. |
214 | packagesNeeded = set() |
215 | |
216 | # Go through the dependencies |
217 | for dependency in XmlList(fpd, "/PlatformSurfaceArea/FrameworkModules/ModuleSA") + \ |
218 | XmlList(fpd, "/PlatformSurfaceArea/FrameworkModules/ModuleSA/Libraries/Instance"): |
822d4f3a |
219 | packagesNeeded.add((Guid(dependency.getAttribute("PackageGuid")), |
220 | dependency.getAttribute("PackageVersion"))) |
312ffece |
221 | |
222 | # Let's see if all the packages are in the workspace |
223 | for guid, version in packagesNeeded: |
224 | # Does anyone provide this package? |
225 | if not fdb.HasPackage((guid, version)) and not (guid, version) in farSpds: |
226 | print ("Error: The fpd %s depends on the package guid %s version %s, which " + \ |
227 | "is not installed in the workspace, nor is it provided by this far.") \ |
228 | % (fpdfile, guid, version) |
229 | installError = True |
4040421a |
230 | |
231 | # Check the fars |
822d4f3a |
232 | thisFarGuid = Guid(XmlElement(manifest, "/FrameworkArchiveManifest/FarHeader/GuidValue")) |
4040421a |
233 | if fdb.HasFar(thisFarGuid): |
312ffece |
234 | if not flags.reinstall: |
235 | print "Error: There is a far with this guid already installed." |
236 | installError = True |
4040421a |
237 | |
238 | # We can not do the install |
239 | if installError: |
312ffece |
240 | if flags.force: |
241 | print "Warning: Ignoring previous errors as you requested." |
4040421a |
242 | else: |
243 | return False |
244 | |
245 | # Install the packages |
246 | for farPackage in XmlList(manifest, "/FrameworkArchiveManifest/FarPackageList/FarPackage"): |
247 | |
248 | filename = XmlElement(farPackage, "FarPackage/FarFilename") |
312ffece |
249 | if not flags.reinstall: |
250 | fdb.AddPackage(filename) |
4040421a |
251 | ExtractFile(far, filename, workspaceLocation) |
252 | zipContents.remove(filename) |
253 | |
312ffece |
254 | DefaultPath = XmlElement(farPackage, "FarPackage/DefaultPath") |
255 | |
4040421a |
256 | for content in XmlList(farPackage, "FarPackage/Contents/FarFilename"): |
257 | |
258 | filename = XmlElementData(content) |
312ffece |
259 | ExtractFile(far, filename, DefaultPath, workspaceLocation, md5sum=content.getAttribute("Md5Sum")) |
260 | zipContents.remove(os.path.join(DefaultPath, filename)) |
4040421a |
261 | |
262 | # Install the platforms |
263 | for farPlatform in XmlList(manifest, "/FrameworkArchiveManifest/FarPlatformList/FarPlatform"): |
264 | |
265 | filename = XmlElement(farPlatform, "FarPlatform/FarFilename") |
312ffece |
266 | if not flags.reinstall: |
267 | fdb.AddPlatform(filename) |
268 | ExtractFile(far, filename, "", workspaceLocation) |
4040421a |
269 | zipContents.remove(filename) |
270 | |
271 | # Install the Contents |
272 | for content in XmlList(manifest, "/FrameworkArchiveManifest/Contents/FarFilename"): |
273 | |
274 | filename = XmlElementData(content) |
312ffece |
275 | ExtractFile(far, filename, "", workspaceLocation) |
4040421a |
276 | zipContents.remove(filename) |
277 | |
278 | # What if there are more files in the far? |
279 | if not zipContents == []: |
312ffece |
280 | print "Warning: There are files in the far that were not expected: ", zipContents |
4040421a |
281 | |
312ffece |
282 | if not flags.reinstall: |
283 | fdb.AddFar(farfile, thisFarGuid) |
4040421a |
284 | |
285 | # If everything has gone well, we can put the manifest file in a safe place... |
286 | farDir = inWorkspace("Tools/Conf/InstalledFars/") |
287 | mkdir(farDir) |
288 | f=open(os.path.join(farDir, thisFarGuid), 'w') |
289 | f.write(far.read("FrameworkArchiveManifest.xml")) |
290 | f.close() |
291 | |
292 | # Write out the new database |
312ffece |
293 | if not flags.reinstall: |
294 | fdb.Write() |
4040421a |
295 | |
296 | far.close() |
297 | |
298 | # This acts like the main() function for the script, unless it is 'import'ed |
299 | # into another script. |
300 | if __name__ == '__main__': |
301 | |
312ffece |
302 | flags = Flags() |
303 | |
4040421a |
304 | # Process the command line args. |
822d4f3a |
305 | optlist, args = getopt.getopt(sys.argv[1:], '?hvfd:', ['directory=', 'help', 'verbose', 'force', 'reinstall']) |
4040421a |
306 | |
307 | # First pass through the options list. |
308 | for o, a in optlist: |
312ffece |
309 | if o in ["-h", "-?", "--help"]: |
4040421a |
310 | print """ |
312ffece |
311 | %s: Install a far (Framework Archive) into the current workspace. |
4040421a |
312 | """ % os.path.basename(sys.argv[0]) |
313 | |
314 | sys.exit() |
315 | optlist.remove((o,a)) |
316 | if o in ["-v", "--verbose"]: |
312ffece |
317 | flags.verbose = True |
822d4f3a |
318 | if o in ["-d", "--directory"]: |
319 | flags.dir = a |
4040421a |
320 | if o in ["-f", "--force"]: |
312ffece |
321 | flags.force = True |
322 | if o in ["--reinstall"]: |
323 | flags.reinstall = True |
4040421a |
324 | |
325 | for f in args: |
326 | InstallFar(f) |
327 | if args == []: |
328 | print "Please pass a far filename on the command line." |