Browse Source

PackageInstaller fixes

David Rose 16 years ago
parent
commit
fcc7b5f9c2

+ 8 - 12
direct/src/p3d/AppRunner.py

@@ -363,15 +363,17 @@ class AppRunner(DirectObject):
     def addPackageInfo(self, name, platform, version, hostUrl):
     def addPackageInfo(self, name, platform, version, hostUrl):
         """ Called by the browser to list all of the "required"
         """ Called by the browser to list all of the "required"
         packages that were preloaded before starting the
         packages that were preloaded before starting the
-        application. """
+        application.  If for some reason the package isn't already
+        downloaded, this will download it on the spot. """
 
 
         host = self.getHost(hostUrl)
         host = self.getHost(hostUrl)
 
 
         try:
         try:
             host.readContentsFile()
             host.readContentsFile()
         except ValueError:
         except ValueError:
-            print "Host %s has not been downloaded, cannot preload %s." % (hostUrl, name)
-            return
+            if not host.downloadContentsFile(self.http):
+                print "Host %s cannot be downloaded, cannot preload %s." % (hostUrl, name)
+                return
 
 
         if not platform:
         if not platform:
             platform = None
             platform = None
@@ -380,15 +382,9 @@ class AppRunner(DirectObject):
             print "Couldn't find %s %s on %s" % (name, version, hostUrl)
             print "Couldn't find %s %s on %s" % (name, version, hostUrl)
             return
             return
 
 
-        self.installedPackages.append(package)
-
-        if package.checkStatus():
-            # The package should have been loaded already.  If it has,
-            # go ahead and mount it.
-            package.installPackage(self)
-        else:
-            print "%s %s is not preloaded." % (
-                package.packageName, package.packageVersion)
+        package.downloadDescFile(self.http)
+        package.downloadPackage(self.http)
+        package.installPackage(self)
 
 
     def setP3DFilename(self, p3dFilename, tokens, argv, instanceId,
     def setP3DFilename(self, p3dFilename, tokens, argv, instanceId,
                        interactiveConsole):
                        interactiveConsole):

+ 25 - 3
direct/src/p3d/PackageInfo.py

@@ -1,4 +1,4 @@
-from pandac.PandaModules import Filename, URLSpec, DocumentSpec, Ramfile, TiXmlDocument, Multifile, Decompressor, EUOk, EUSuccess, VirtualFileSystem, Thread, getModelPath, Patchfile
+from pandac.PandaModules import Filename, URLSpec, DocumentSpec, Ramfile, TiXmlDocument, Multifile, Decompressor, EUOk, EUSuccess, VirtualFileSystem, Thread, getModelPath, Patchfile, ExecutionEnvironment
 from direct.p3d.FileSpec import FileSpec
 from direct.p3d.FileSpec import FileSpec
 from direct.showbase import VFSImporter
 from direct.showbase import VFSImporter
 import os
 import os
@@ -72,6 +72,10 @@ class PackageInfo:
         # downloaded and unpackaged.
         # downloaded and unpackaged.
         self.hasPackage = False
         self.hasPackage = False
 
 
+        # This is set true when the package has been "installed",
+        # meaning it's been added to the paths and all.
+        self.installed = False
+
     def getDownloadEffort(self):
     def getDownloadEffort(self):
         """ Returns the relative amount of effort it will take to
         """ Returns the relative amount of effort it will take to
         download this package.  The units are meaningless, except
         download this package.  The units are meaningless, except
@@ -348,7 +352,10 @@ class PackageInfo:
         patchMaker.buildPatchChains()
         patchMaker.buildPatchChains()
         fromPv = patchMaker.getPackageVersion(package.getGenericKey(fileSpec))
         fromPv = patchMaker.getPackageVersion(package.getGenericKey(fileSpec))
         toPv = package.currentPv
         toPv = package.currentPv
-        patchChain = toPv.getPatchChain(fromPv)
+
+        patchChain = None
+        if toPv and fromPv:
+            patchChain = toPv.getPatchChain(fromPv)
 
 
         if patchChain is None:
         if patchChain is None:
             # No path.
             # No path.
@@ -383,6 +390,8 @@ class PackageInfo:
         targetPathname.setBinary()
         targetPathname.setBinary()
         
         
         channel = self.http.makeChannel(False)
         channel = self.http.makeChannel(False)
+        # TODO: check for a previous partial download, and resume it.
+        targetPathname.unlink()
         channel.beginGetDocument(url)
         channel.beginGetDocument(url)
         channel.downloadToFile(targetPathname)
         channel.downloadToFile(targetPathname)
         while channel.run():
         while channel.run():
@@ -522,7 +531,11 @@ class PackageInfo:
         available for use. """
         available for use. """
 
 
         assert self.hasPackage
         assert self.hasPackage
-        
+        if self.installed:
+            # Already installed.
+            return True
+        assert self not in appRunner.installedPackages
+
         mfPathname = Filename(self.packageDir, self.uncompressedArchive.filename)
         mfPathname = Filename(self.packageDir, self.uncompressedArchive.filename)
         mf = Multifile()
         mf = Multifile()
         if not mf.openRead(mfPathname):
         if not mf.openRead(mfPathname):
@@ -562,6 +575,10 @@ class PackageInfo:
         # model-path, so it shouldn't be already there.
         # model-path, so it shouldn't be already there.
         getModelPath().prependDirectory(self.packageDir)
         getModelPath().prependDirectory(self.packageDir)
 
 
+        # Set the environment variable to reference the package root.
+        envvar = '%s_ROOT' % (self.packageName.upper())
+        ExecutionEnvironment.setEnvironmentVariable(envvar, self.packageDir.toOsSpecific())
+
         # Also, find any toplevel Python packages, and add these as
         # Also, find any toplevel Python packages, and add these as
         # shared packages.  This will allow different packages
         # shared packages.  This will allow different packages
         # installed in different directories to share Python files as
         # installed in different directories to share Python files as
@@ -577,3 +594,8 @@ class PackageInfo:
         # Fix up any shared directories so we can load packages from
         # Fix up any shared directories so we can load packages from
         # disparate locations.
         # disparate locations.
         VFSImporter.reloadSharedPackages()
         VFSImporter.reloadSharedPackages()
+
+        self.installed = True
+        appRunner.installedPackages.append(self)
+
+        return True

+ 84 - 26
direct/src/p3d/PackageInstaller.py

@@ -2,6 +2,7 @@ from direct.showbase.DirectObject import DirectObject
 from direct.stdpy.threading import Lock
 from direct.stdpy.threading import Lock
 from direct.showbase.MessengerGlobal import messenger
 from direct.showbase.MessengerGlobal import messenger
 from direct.task.TaskManagerGlobal import taskMgr
 from direct.task.TaskManagerGlobal import taskMgr
+from direct.p3d.PackageInfo import PackageInfo
 
 
 class PackageInstaller(DirectObject):
 class PackageInstaller(DirectObject):
 
 
@@ -43,12 +44,23 @@ class PackageInstaller(DirectObject):
             self.version = version
             self.version = version
             self.host = host
             self.host = host
 
 
-            # Filled in by getDescFile().
-            self.package = None
+            # This will be filled in properly by checkDescFile() or
+            # getDescFile(); in the meantime, set a placeholder.
+            self.package = PackageInfo(host, packageName, version)
 
 
+            # Set true when the package has finished downloading,
+            # either successfully or unsuccessfully.
             self.done = False
             self.done = False
+
+            # Set true or false when self.done has been set.
             self.success = False
             self.success = False
 
 
+            # Set true when the packageFinished() callback has been
+            # delivered.
+            self.notified = False
+
+            # These are used to ensure the callbacks only get
+            # delivered once for a particular package.
             self.calledPackageStarted = False
             self.calledPackageStarted = False
             self.calledPackageFinished = False
             self.calledPackageFinished = False
 
 
@@ -59,7 +71,8 @@ class PackageInstaller(DirectObject):
             # which is weighted differently into one grand total.  So,
             # which is weighted differently into one grand total.  So,
             # the total doesn't really represent bytes; it's a
             # the total doesn't really represent bytes; it's a
             # unitless number, which means something only as a ratio
             # unitless number, which means something only as a ratio
-            # to other packages.
+            # to other packages.  Filled in by checkDescFile() or
+            # getDescFile().
             self.downloadEffort = 0
             self.downloadEffort = 0
 
 
         def getProgress(self):
         def getProgress(self):
@@ -68,6 +81,33 @@ class PackageInstaller(DirectObject):
 
 
             return self.package.downloadProgress
             return self.package.downloadProgress
 
 
+        def checkDescFile(self):
+            """ Returns true if the desc file is already downloaded
+            and good, or false if it needs to be downloaded. """
+
+            if not self.host.hasContentsFile:
+                # If the contents file isn't ready yet, we can't check
+                # the desc file yet.
+                return False
+
+            # All right, get the package info now.
+            package = self.host.getPackage(self.packageName, self.version)
+            if not package:
+                print "Package %s %s not known on %s" % (
+                    self.packageName, self.version, self.host.hostUrl)
+                return False
+
+            self.package = package
+            self.package.checkStatus()
+
+            if not self.package.hasDescFile:
+                return False
+
+            self.downloadEffort = self.package.getDownloadEffort()
+
+            return True
+            
+
         def getDescFile(self, http):
         def getDescFile(self, http):
             """ Synchronously downloads the desc files required for
             """ Synchronously downloads the desc files required for
             the package. """
             the package. """
@@ -76,12 +116,13 @@ class PackageInstaller(DirectObject):
                 return False
                 return False
 
 
             # All right, get the package info now.
             # All right, get the package info now.
-            self.package = self.host.getPackage(self.packageName, self.version)
-            if not self.package:
+            package = self.host.getPackage(self.packageName, self.version)
+            if not package:
                 print "Package %s %s not known on %s" % (
                 print "Package %s %s not known on %s" % (
                     self.packageName, self.version, self.host.hostUrl)
                     self.packageName, self.version, self.host.hostUrl)
                 return False
                 return False
 
 
+            self.package = package
             if not self.package.downloadDescFile(http):
             if not self.package.downloadDescFile(http):
                 return False
                 return False
 
 
@@ -125,6 +166,10 @@ class PackageInstaller(DirectObject):
         self.needsDownload = []
         self.needsDownload = []
         self.downloadTask = None
         self.downloadTask = None
 
 
+        # A list of packages that were already done at the time they
+        # were passed to addPackage().
+        self.earlyDone = []
+
         # A list of packages that have been successfully installed, or
         # A list of packages that have been successfully installed, or
         # packages that have failed.
         # packages that have failed.
         self.done = []
         self.done = []
@@ -184,11 +229,23 @@ class PackageInstaller(DirectObject):
         self.packageLock.acquire()
         self.packageLock.acquire()
         try:
         try:
             self.packages.append(pp)
             self.packages.append(pp)
-            self.needsDescFile.append(pp)
-            if not self.descFileTask:
-                self.descFileTask = taskMgr.add(
-                    self.__getDescFileTask, 'getDescFile',
-                    taskChain = self.taskChain)
+            if not pp.checkDescFile():
+                # Still need to download the desc file.
+                self.needsDescFile.append(pp)
+                if not self.descFileTask:
+                    self.descFileTask = taskMgr.add(
+                        self.__getDescFileTask, 'getDescFile',
+                        taskChain = self.taskChain)
+
+            elif not pp.package.hasPackage:
+                # The desc file is good, but the package itself needs
+                # to be downloaded.
+                self.needsDownload.append(pp)
+
+            else:
+                # The package is already fully downloaded.
+                self.earlyDone.append(pp)
+                    
         finally:
         finally:
             self.packageLock.release()
             self.packageLock.release()
 
 
@@ -197,15 +254,19 @@ class PackageInstaller(DirectObject):
         installed, call donePackages() to mark the end of the list.
         installed, call donePackages() to mark the end of the list.
         This is necessary to determine what the complete set of
         This is necessary to determine what the complete set of
         packages is (and therefore how large the total download size
         packages is (and therefore how large the total download size
-        is).  Until this is called, no low-level callbacks will be
-        made as the packages are downloading. """
+        is).  None of the low-level callbacks will be made before this
+        call. """
 
 
         if self.state != self.S_initial:
         if self.state != self.S_initial:
             # We've already been here.
             # We've already been here.
             return
             return
 
 
-        working = True
-        
+        # Throw the messages for packages that were already done
+        # before we started.
+        for pp in self.earlyDone:
+            self.__donePackage(pp, True)
+        self.earlyDone = []
+
         self.packageLock.acquire()
         self.packageLock.acquire()
         try:
         try:
             if self.state != self.S_initial:
             if self.state != self.S_initial:
@@ -213,12 +274,13 @@ class PackageInstaller(DirectObject):
             self.state = self.S_ready
             self.state = self.S_ready
             if not self.needsDescFile:
             if not self.needsDescFile:
                 # All package desc files are already available; so begin.
                 # All package desc files are already available; so begin.
-                working = self.__prepareToStart()
+                self.__prepareToStart()
         finally:
         finally:
             self.packageLock.release()
             self.packageLock.release()
 
 
-        if not working:
-            self.downloadFinished(True)
+        if not self.packages:
+            # Trivial no-op.
+            self.__callDownloadFinished(True)
 
 
     def downloadStarted(self):
     def downloadStarted(self):
         """ This callback is made at some point after donePackages()
         """ This callback is made at some point after donePackages()
@@ -321,20 +383,15 @@ class PackageInstaller(DirectObject):
         downloaded and installed, or has failed. """
         downloaded and installed, or has failed. """
         print "Downloaded %s: %s" % (pp.packageName, pp.success)
         print "Downloaded %s: %s" % (pp.packageName, pp.success)
         self.__callPackageFinished(pp, pp.success)
         self.__callPackageFinished(pp, pp.success)
+        pp.notified = True
 
 
-        if not pp.calledPackageStarted:
-            # Trivially done; this one was done before it got started.
-            return
-
-        assert self.state == self.S_started
         # See if there are more packages to go.
         # See if there are more packages to go.
         success = True
         success = True
         allDone = True
         allDone = True
         self.packageLock.acquire()
         self.packageLock.acquire()
         try:
         try:
-            assert self.state == self.S_started
             for pp in self.packages:
             for pp in self.packages:
-                if pp.done:
+                if pp.notified:
                     success = success and pp.success
                     success = success and pp.success
                 else:
                 else:
                     allDone = False
                     allDone = False
@@ -468,8 +525,6 @@ class PackageInstaller(DirectObject):
             self.__donePackage(pp, False)
             self.__donePackage(pp, False)
             return task.cont
             return task.cont
 
 
-        pp.package.installPackage(self.appRunner)
-
         # Successfully downloaded and installed.
         # Successfully downloaded and installed.
         self.__donePackage(pp, True)
         self.__donePackage(pp, True)
         
         
@@ -480,6 +535,9 @@ class PackageInstaller(DirectObject):
         or otherwise. """
         or otherwise. """
         assert not pp.done
         assert not pp.done
 
 
+        if success:
+            pp.package.installPackage(self.appRunner)
+
         self.packageLock.acquire()
         self.packageLock.acquire()
         try:
         try:
             pp.done = True
             pp.done = True

+ 1 - 0
direct/src/p3d/Packager.py

@@ -1392,6 +1392,7 @@ class Packager:
                     file.newName = file.newName[:-1] + 'e'
                     file.newName = file.newName[:-1] + 'e'
                 
                 
                 preFilename = Filename.temporary('', 'p3d_', '.pre')
                 preFilename = Filename.temporary('', 'p3d_', '.pre')
+                tempFilename.setText()
                 encryptFile(tempFilename, preFilename, self.packager.prcEncryptionKey)
                 encryptFile(tempFilename, preFilename, self.packager.prcEncryptionKey)
                 tempFilename.unlink()
                 tempFilename.unlink()
                 tempFilename = preFilename
                 tempFilename = preFilename

+ 2 - 2
direct/src/p3d/packp3d.py

@@ -7,8 +7,8 @@ tree of .py files and models, into a p3d file for convenient
 distribution.  The resulting p3d file can be run by the Panda3D
 distribution.  The resulting p3d file can be run by the Panda3D
 runtime executable, or by the Panda3D web browser plugin.
 runtime executable, or by the Panda3D web browser plugin.
 
 
-Also see ppackage, which can be used to build p3d files more
-generally, using a pdef description file.
+Also see ppackage, a more powerful (but more complex) tool that can
+also be used to build p3d applications, using a pdef description file.
 
 
 Usage:
 Usage:
 
 

+ 3 - 7
direct/src/p3d/ppackage.py

@@ -12,13 +12,9 @@ file, for execution by the Panda3D plugin or runtime.  (But also see
 packp3d, which is designed to be a simpler interface for building
 packp3d, which is designed to be a simpler interface for building
 applications.)
 applications.)
 
 
-In addition to building a package in the first place, this script will
-also generate downloadable patches of the package against previous
-versions, and manage the whole tree of patches in a directory
-structure suitable for hosting on a web server somewhere.  (This part
-is not yet completed.)
-
-This script is actually a wrapper around Panda's Packager.py.
+This script is actually a wrapper around Panda's Packager.py, which
+can be invoked directly by Python code that requires a programmatic
+interface to building packages.
 
 
 Usage:
 Usage:
 
 

+ 4 - 2
direct/src/showbase/ShowBase.py

@@ -54,7 +54,7 @@ class ShowBase(DirectObject.DirectObject):
 
 
     notify = directNotify.newCategory("ShowBase")
     notify = directNotify.newCategory("ShowBase")
 
 
-    def __init__(self, fStartDirect = True):
+    def __init__(self, fStartDirect = True, windowType = None):
         __builtin__.__dev__ = config.GetBool('want-dev', 0)
         __builtin__.__dev__ = config.GetBool('want-dev', 0)
         if config.GetBool('want-variable-dump', 0):
         if config.GetBool('want-variable-dump', 0):
             ExceptionVarDump.install()
             ExceptionVarDump.install()
@@ -139,7 +139,9 @@ class ShowBase(DirectObject.DirectObject):
         # we get a window-event.
         # we get a window-event.
         self.__oldAspectRatio = None
         self.__oldAspectRatio = None
 
 
-        self.windowType = self.config.GetString('window-type', 'onscreen')
+        self.windowType = windowType
+        if self.windowType is None:
+            self.windowType = self.config.GetString('window-type', 'onscreen')
         self.requireWindow = self.config.GetBool('require-window', 1)
         self.requireWindow = self.config.GetBool('require-window', 1)
 
 
         # base.win is the main, or only window; base.winList is a list of
         # base.win is the main, or only window; base.winList is a list of