def __init__(self, pathname, ignoreUsageXml=False): self.pathname = pathname self.filenames = [] self.fileSize = 0 self.nested = [] self.nestedSize = 0 xusage = None if not ignoreUsageXml: # Look for a usage.xml file in this directory. If we find # one, we read it for the file size and then stop here, as # an optimization. usageFilename = Filename(pathname, 'usage.xml') doc = TiXmlDocument(usageFilename.toOsSpecific()) if doc.LoadFile(): xusage = doc.FirstChildElement('usage') if xusage: diskSpace = xusage.Attribute('disk_space') try: diskSpace = int(diskSpace or '') except ValueError: diskSpace = None if diskSpace is not None: self.fileSize = diskSpace return files = vfs.scanDirectory(self.pathname) if files is None: files = [] for vfile in files: if hasattr(vfile, 'getMount'): if not isinstance(vfile.getMount(), VirtualFileMountSystem): # Not a real file; ignore it. continue if vfile.isDirectory(): # A nested directory. subdir = ScanDirectoryNode(vfile.getFilename(), ignoreUsageXml=ignoreUsageXml) self.nested.append(subdir) self.nestedSize += subdir.getTotalSize() elif vfile.isRegularFile(): # A nested file. self.filenames.append(vfile.getFilename()) self.fileSize += vfile.getFileSize() else: # Some other wacky file thing. self.filenames.append(vfile.getFilename()) if xusage: # Now update the usage.xml file with the newly-determined # disk space. xusage.SetAttribute('disk_space', str(self.getTotalSize())) tfile = Filename.temporary(pathname.cStr(), '.xml') if doc.SaveFile(tfile.toOsSpecific()): tfile.renameTo(usageFilename)
def getUsage(self): """ Returns the xusage element that is read from the usage.xml file, or None if there is no usage.xml file. """ if not hasattr(PandaModules, 'TiXmlDocument'): return None filename = Filename(self.getPackageDir(), self.UsageBasename) doc = TiXmlDocument(filename.toOsSpecific()) if not doc.LoadFile(): return None xusage = doc.FirstChildElement('usage') if not xusage: return None return copy.copy(xusage)
def readConfigXml(self): """ Reads the config.xml file that may be present in the root directory. """ if not hasattr(PandaModules, 'TiXmlDocument'): return from pandac.PandaModules import TiXmlDocument filename = Filename(self.rootDir, self.ConfigBasename) doc = TiXmlDocument(filename.toOsSpecific()) if not doc.LoadFile(): return xconfig = doc.FirstChildElement('config') if xconfig: maxDiskUsage = xconfig.Attribute('max_disk_usage') try: self.maxDiskUsage = int(maxDiskUsage or '') except ValueError: pass
def writeConfigXml(self): """ Rewrites the config.xml to the root directory. This isn't called automatically; an application may call this after adjusting some parameters (such as self.maxDiskUsage). """ from pandac.PandaModules import TiXmlDocument, TiXmlDeclaration, TiXmlElement filename = Filename(self.rootDir, self.ConfigBasename) doc = TiXmlDocument(filename.toOsSpecific()) decl = TiXmlDeclaration("1.0", "utf-8", "") doc.InsertEndChild(decl) xconfig = TiXmlElement('config') xconfig.SetAttribute('max_disk_usage', str(self.maxDiskUsage)) doc.InsertEndChild(xconfig) # Write the file to a temporary filename, then atomically move # it to its actual filename, to avoid race conditions when # updating this file. tfile = Filename.temporary(self.rootDir.cStr(), '.xml') if doc.SaveFile(tfile.toOsSpecific()): tfile.renameTo(filename)
def markUsed(self): """ Marks the package as having been used. This is normally called automatically by installPackage(). """ if not hasattr(PandaModules, 'TiXmlDocument'): return if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever: # Not allowed to write any files to the package directory. return if self.updated: # If we've just installed a new version of the package, # re-measure the actual disk space used. self.diskSpace = self.__measureDiskSpace() filename = Filename(self.getPackageDir(), self.UsageBasename) doc = TiXmlDocument(filename.toOsSpecific()) if not doc.LoadFile(): decl = TiXmlDeclaration("1.0", "utf-8", "") doc.InsertEndChild(decl) xusage = doc.FirstChildElement('usage') if not xusage: doc.InsertEndChild(TiXmlElement('usage')) xusage = doc.FirstChildElement('usage') now = int(time.time()) count = xusage.Attribute('count_app') try: count = int(count or '') except ValueError: count = 0 xusage.SetAttribute('first_use', str(now)) count += 1 xusage.SetAttribute('count_app', str(count)) xusage.SetAttribute('last_use', str(now)) if self.updated: xusage.SetAttribute('last_update', str(now)) self.updated = False else: # Since we haven't changed the disk space, we can just # read it from the previous xml file. diskSpace = xusage.Attribute('disk_space') try: diskSpace = int(diskSpace or '') except ValueError: # Unless it wasn't set already. self.diskSpace = self.__measureDiskSpace() xusage.SetAttribute('disk_space', str(self.diskSpace)) # Write the file to a temporary filename, then atomically move # it to its actual filename, to avoid race conditions when # updating this file. tfile = Filename.temporary(self.getPackageDir().cStr(), '.xml') if doc.SaveFile(tfile.toOsSpecific()): tfile.renameTo(filename)
def installPackagesInto(self, hostDir, platform): """ Installs the packages required by the .p3d file into the specified directory, for the given platform. """ if not self.includeRequires: return pkgTree = PackageTree(platform, hostDir, self.hostUrl) pkgTree.installPackage("images", None, self.standalone.host.hostUrl) for name, version, hostUrl in self.requires: pkgTree.installPackage(name, version, hostUrl) # Remove the extracted files from the compressed archive, to save space. vfs = VirtualFileSystem.getGlobalPtr() for package in pkgTree.packages.values(): if package.uncompressedArchive: archive = Filename(package.getPackageDir(), package.uncompressedArchive.filename) if not archive.exists(): continue mf = Multifile() # Make sure that it isn't mounted before altering it, just to be safe vfs.unmount(archive) os.chmod(archive.toOsSpecific(), 0644) if not mf.openReadWrite(archive): Installer.notify.warning("Failed to open archive %s" % (archive)) continue # We don't iterate over getNumSubfiles because we're # removing subfiles while we're iterating over them. subfiles = mf.getSubfileNames() for subfile in subfiles: # We do *NOT* call vfs.exists here in case the package is mounted. if Filename(package.getPackageDir(), subfile).exists(): Installer.notify.debug( "Removing already-extracted %s from multifile" % (subfile)) mf.removeSubfile(subfile) # This seems essential for mf.close() not to crash later. mf.repack() # If we have no subfiles left, we can just remove the multifile. #if mf.getNumSubfiles() == 0: # Installer.notify.info("Removing empty archive %s" % (package.uncompressedArchive.filename)) # mf.close() # archive.unlink() #else: mf.close() try: os.chmod(archive.toOsSpecific(), 0444) except: pass # Write out our own contents.xml file. doc = TiXmlDocument() decl = TiXmlDeclaration("1.0", "utf-8", "") doc.InsertEndChild(decl) xcontents = TiXmlElement("contents") for package in pkgTree.packages.values(): xpackage = TiXmlElement('package') xpackage.SetAttribute('name', package.packageName) if package.platform: xpackage.SetAttribute('platform', package.platform) assert package.platform == platform if package.packageVersion: xpackage.SetAttribute('version', version) xpackage.SetAttribute( 'filename', package.packageName + "/" + package.packageVersion + "/" + package.descFileBasename) else: xpackage.SetAttribute( 'filename', package.packageName + "/" + package.descFileBasename) xcontents.InsertEndChild(xpackage) doc.InsertEndChild(xcontents) doc.SaveFile(Filename(hostDir, "contents.xml").toOsSpecific())