Browse Source

Merge branch 'release/1.9.x'

Conflicts:
	direct/src/p3d/Packager.py
	direct/src/p3d/ppackage.py
	makepanda/makepandacore.py
rdb 10 years ago
parent
commit
0a9f9887f9

+ 4 - 2
direct/src/actor/Actor.py

@@ -1532,8 +1532,10 @@ class Actor(DirectObject, NodePath):
 
 
     # actions
     # actions
     def animPanel(self):
     def animPanel(self):
-        from direct.showbase import TkGlobal
-        from direct.tkpanels import AnimPanel
+        # Don't use a regular import, to prevent ModuleFinder from picking
+        # it up as a dependency when building a .p3d package.
+        import importlib
+        AnimPanel = importlib.import_module('direct.tkpanels.AnimPanel')
         return AnimPanel.AnimPanel(self)
         return AnimPanel.AnimPanel(self)
 
 
     def stop(self, animName=None, partName=None):
     def stop(self, animName=None, partName=None):

+ 6 - 3
direct/src/directnotify/DirectNotify.py

@@ -111,10 +111,13 @@ class DirectNotify:
             category.setWarning(1)
             category.setWarning(1)
             category.setInfo(1)
             category.setInfo(1)
             category.setDebug(1)
             category.setDebug(1)
-            
+
     def popupControls(self, tl = None):
     def popupControls(self, tl = None):
-        from direct.tkpanels import NotifyPanel
+        # Don't use a regular import, to prevent ModuleFinder from picking
+        # it up as a dependency when building a .p3d package.
+        import importlib
+        NotifyPanel = importlib.import_module('direct.tkpanels.NotifyPanel')
         NotifyPanel.NotifyPanel(self, tl)
         NotifyPanel.NotifyPanel(self, tl)
-        
+
     def giveNotify(self,cls):
     def giveNotify(self,cls):
         cls.notify = self.newCategory(cls.__name__)
         cls.notify = self.newCategory(cls.__name__)

+ 20 - 16
direct/src/extensions_native/CInterval_extensions.py

@@ -60,13 +60,17 @@ def popupControls(self, tl = None):
         """
         """
         Popup control panel for interval.
         Popup control panel for interval.
         """
         """
-        from direct.showbase.TkGlobal import Toplevel, Frame, Button, LEFT, X, Pmw
         import math
         import math
-        from direct.tkwidgets import EntryScale
+        # Don't use a regular import, to prevent ModuleFinder from picking
+        # it up as a dependency when building a .p3d package.
+        import importlib
+        EntryScale = importlib.import_module('direct.tkwidgets.EntryScale')
+        Tkinter = importlib.import_module('Tkinter')
+
         if tl == None:
         if tl == None:
-            tl = Toplevel()
+            tl = Tkinter.Toplevel()
             tl.title('Interval Controls')
             tl.title('Interval Controls')
-        outerFrame = Frame(tl)
+        outerFrame = Tkinter.Frame(tl)
         def entryScaleCommand(t, s=self):
         def entryScaleCommand(t, s=self):
             s.setT(t)
             s.setT(t)
             s.pause()
             s.pause()
@@ -75,8 +79,8 @@ def popupControls(self, tl = None):
             min = 0, max = math.floor(self.getDuration() * 100) / 100,
             min = 0, max = math.floor(self.getDuration() * 100) / 100,
             command = entryScaleCommand)
             command = entryScaleCommand)
         es.set(self.getT(), fCommand = 0)
         es.set(self.getT(), fCommand = 0)
-        es.pack(expand = 1, fill = X)
-        bf = Frame(outerFrame)
+        es.pack(expand = 1, fill = Tkinter.X)
+        bf = Tkinter.Frame(outerFrame)
         # Jump to start and end
         # Jump to start and end
         def toStart(s=self, es=es):
         def toStart(s=self, es=es):
             s.setT(0.0)
             s.setT(0.0)
@@ -84,23 +88,23 @@ def popupControls(self, tl = None):
         def toEnd(s=self):
         def toEnd(s=self):
             s.setT(s.getDuration())
             s.setT(s.getDuration())
             s.pause()
             s.pause()
-        jumpToStart = Button(bf, text = '<<', command = toStart)
+        jumpToStart = Tkinter.Button(bf, text = '<<', command = toStart)
         # Stop/play buttons
         # Stop/play buttons
         def doPlay(s=self, es=es):
         def doPlay(s=self, es=es):
             s.resume(es.get())
             s.resume(es.get())
 
 
-        stop = Button(bf, text = 'Stop',
+        stop = Tkinter.Button(bf, text = 'Stop',
                       command = lambda s=self: s.pause())
                       command = lambda s=self: s.pause())
-        play = Button(
+        play = Tkinter.Button(
             bf, text = 'Play',
             bf, text = 'Play',
             command = doPlay)
             command = doPlay)
-        jumpToEnd = Button(bf, text = '>>', command = toEnd)
-        jumpToStart.pack(side = LEFT, expand = 1, fill = X)
-        play.pack(side = LEFT, expand = 1, fill = X)
-        stop.pack(side = LEFT, expand = 1, fill = X)
-        jumpToEnd.pack(side = LEFT, expand = 1, fill = X)
-        bf.pack(expand = 1, fill = X)
-        outerFrame.pack(expand = 1, fill = X)
+        jumpToEnd = Tkinter.Button(bf, text = '>>', command = toEnd)
+        jumpToStart.pack(side = Tkinter.LEFT, expand = 1, fill = Tkinter.X)
+        play.pack(side = Tkinter.LEFT, expand = 1, fill = Tkinter.X)
+        stop.pack(side = Tkinter.LEFT, expand = 1, fill = Tkinter.X)
+        jumpToEnd.pack(side = Tkinter.LEFT, expand = 1, fill = Tkinter.X)
+        bf.pack(expand = 1, fill = Tkinter.X)
+        outerFrame.pack(expand = 1, fill = Tkinter.X)
         # Add function to update slider during setT calls
         # Add function to update slider during setT calls
         def update(t, es=es):
         def update(t, es=es):
             es.set(t, fCommand = 0)
             es.set(t, fCommand = 0)

+ 12 - 3
direct/src/extensions_native/NodePath_extensions.py

@@ -407,7 +407,10 @@ del iPosHprScale
 #####################################################################
 #####################################################################
 def place(self):
 def place(self):
         base.startDirect(fWantTk = 1)
         base.startDirect(fWantTk = 1)
-        from direct.tkpanels import Placer
+        # Don't use a regular import, to prevent ModuleFinder from picking
+        # it up as a dependency when building a .p3d package.
+        import importlib
+        Placer = importlib.import_module('direct.tkpanels.Placer')
         return Placer.place(self)
         return Placer.place(self)
 
 
 Dtool_funcToMethod(place, NodePath)
 Dtool_funcToMethod(place, NodePath)
@@ -415,7 +418,10 @@ del place
 #####################################################################
 #####################################################################
 def explore(self):
 def explore(self):
         base.startDirect(fWantTk = 1)
         base.startDirect(fWantTk = 1)
-        from direct.tkwidgets import SceneGraphExplorer
+        # Don't use a regular import, to prevent ModuleFinder from picking
+        # it up as a dependency when building a .p3d package.
+        import importlib
+        SceneGraphExplorer = importlib.import_module('direct.tkwidgets.SceneGraphExplorer')
         return SceneGraphExplorer.explore(self)
         return SceneGraphExplorer.explore(self)
 
 
 Dtool_funcToMethod(explore, NodePath)
 Dtool_funcToMethod(explore, NodePath)
@@ -423,7 +429,10 @@ del explore
 #####################################################################
 #####################################################################
 def rgbPanel(self, cb = None):
 def rgbPanel(self, cb = None):
         base.startTk()
         base.startTk()
-        from direct.tkwidgets import Slider
+        # Don't use a regular import, to prevent ModuleFinder from picking
+        # it up as a dependency when building a .p3d package.
+        import importlib
+        Slider = importlib.import_module('direct.tkwidgets.Slider')
         return Slider.rgbPanel(self, cb)
         return Slider.rgbPanel(self, cb)
 
 
 Dtool_funcToMethod(rgbPanel, NodePath)
 Dtool_funcToMethod(rgbPanel, NodePath)

+ 4 - 1
direct/src/fsm/ClassicFSM.py

@@ -369,7 +369,10 @@ class ClassicFSM(DirectObject):
             return 0
             return 0
 
 
     def view(self):
     def view(self):
-        from direct.tkpanels import FSMInspector
+        # Don't use a regular import, to prevent ModuleFinder from picking
+        # it up as a dependency when building a .p3d package.
+        import importlib
+        FSMInspector = importlib.import_module('direct.tkpanels.FSMInspector')
         FSMInspector.FSMInspector(self)
         FSMInspector.FSMInspector(self)
 
 
     def isInternalStateInFlux(self):
     def isInternalStateInFlux(self):

+ 20 - 20
direct/src/interval/Interval.py

@@ -444,16 +444,16 @@ class Interval(DirectObject):
         """
         """
         Popup control panel for interval.
         Popup control panel for interval.
         """
         """
-        from direct.showbase import TkGlobal
-        import math
-        # I moved this here because Toontown does not ship Tk
-        from Tkinter import Toplevel, Frame, Button, LEFT, X
-        import Pmw
-        from direct.tkwidgets import EntryScale
+        # Don't use a regular import, to prevent ModuleFinder from picking
+        # it up as a dependency when building a .p3d package.
+        import importlib
+        EntryScale = importlib.import_module('direct.tkwidgets.EntryScale')
+        Tkinter = importlib.import_module('Tkinter')
+
         if tl == None:
         if tl == None:
-            tl = Toplevel()
+            tl = Tkinter.Toplevel()
             tl.title('Interval Controls')
             tl.title('Interval Controls')
-        outerFrame = Frame(tl)
+        outerFrame = Tkinter.Frame(tl)
         def entryScaleCommand(t, s=self):
         def entryScaleCommand(t, s=self):
             s.setT(t)
             s.setT(t)
             s.pause()
             s.pause()
@@ -462,8 +462,8 @@ class Interval(DirectObject):
             min = 0, max = math.floor(self.getDuration() * 100) / 100,
             min = 0, max = math.floor(self.getDuration() * 100) / 100,
             command = entryScaleCommand)
             command = entryScaleCommand)
         es.set(self.getT(), fCommand = 0)
         es.set(self.getT(), fCommand = 0)
-        es.pack(expand = 1, fill = X)
-        bf = Frame(outerFrame)
+        es.pack(expand = 1, fill = Tkinter.X)
+        bf = Tkinter.Frame(outerFrame)
         # Jump to start and end
         # Jump to start and end
         def toStart(s=self, es=es):
         def toStart(s=self, es=es):
             s.clearToInitial()
             s.clearToInitial()
@@ -473,23 +473,23 @@ class Interval(DirectObject):
             s.setT(s.getDuration())
             s.setT(s.getDuration())
             es.set(s.getDuration(), fCommand = 0)
             es.set(s.getDuration(), fCommand = 0)
             s.pause()
             s.pause()
-        jumpToStart = Button(bf, text = '<<', command = toStart)
+        jumpToStart = Tkinter.Button(bf, text = '<<', command = toStart)
         # Stop/play buttons
         # Stop/play buttons
         def doPlay(s=self, es=es):
         def doPlay(s=self, es=es):
             s.resume(es.get())
             s.resume(es.get())
 
 
-        stop = Button(bf, text = 'Stop',
+        stop = Tkinter.Button(bf, text = 'Stop',
                       command = lambda s=self: s.pause())
                       command = lambda s=self: s.pause())
-        play = Button(
+        play = Tkinter.Button(
             bf, text = 'Play',
             bf, text = 'Play',
             command = doPlay)
             command = doPlay)
-        jumpToEnd = Button(bf, text = '>>', command = toEnd)
-        jumpToStart.pack(side = LEFT, expand = 1, fill = X)
-        play.pack(side = LEFT, expand = 1, fill = X)
-        stop.pack(side = LEFT, expand = 1, fill = X)
-        jumpToEnd.pack(side = LEFT, expand = 1, fill = X)
-        bf.pack(expand = 1, fill = X)
-        outerFrame.pack(expand = 1, fill = X)
+        jumpToEnd = Tkinter.Button(bf, text = '>>', command = toEnd)
+        jumpToStart.pack(side = Tkinter.LEFT, expand = 1, fill = Tkinter.X)
+        play.pack(side = Tkinter.LEFT, expand = 1, fill = Tkinter.X)
+        stop.pack(side = Tkinter.LEFT, expand = 1, fill = Tkinter.X)
+        jumpToEnd.pack(side = Tkinter.LEFT, expand = 1, fill = Tkinter.X)
+        bf.pack(expand = 1, fill = Tkinter.X)
+        outerFrame.pack(expand = 1, fill = Tkinter.X)
         # Add function to update slider during setT calls
         # Add function to update slider during setT calls
         def update(t, es=es):
         def update(t, es=es):
             es.set(t, fCommand = 0)
             es.set(t, fCommand = 0)

+ 1 - 40
direct/src/p3d/AppRunner.py

@@ -424,47 +424,8 @@ class AppRunner(DirectObject):
         it downloads a new version on-the-spot.  Returns true on
         it downloads a new version on-the-spot.  Returns true on
         success, false on failure. """
         success, false on failure. """
 
 
-        if fileSpec.quickVerify(pathname = localPathname):
-            # It's good, keep it.
-            return True
-
         assert self.http
         assert self.http
-
-        # It's stale, get a new one.
-        doc = None
-        if self.superMirrorUrl:
-            # Use the "super mirror" first.
-            url = core.URLSpec(self.superMirrorUrl + fileSpec.filename)
-            self.notify.info("Freshening %s" % (url))
-            doc = self.http.getDocument(url)
-
-        if not doc or not doc.isValid():
-            # Failing the super mirror, contact the actual host.
-            url = core.URLSpec(host.hostUrlPrefix + fileSpec.filename)
-            self.notify.info("Freshening %s" % (url))
-            doc = self.http.getDocument(url)
-            if not doc.isValid():
-                return False
-
-        file = Filename.temporary('', 'p3d_')
-        if not doc.downloadToFile(file):
-            # Failed to download.
-            file.unlink()
-            return False
-
-        # Successfully downloaded!
-        localPathname.makeDir()
-        if not file.renameTo(localPathname):
-            # Couldn't move it into place.
-            file.unlink()
-            return False
-
-        if not fileSpec.fullVerify(pathname = localPathname, notify = self.notify):
-            # No good after download.
-            self.notify.info("%s is still no good after downloading." % (url))
-            return False
-
-        return True
+        return host.freshenFile(self.http, fileSpec, localPathname)
 
 
     def scanInstalledPackages(self):
     def scanInstalledPackages(self):
         """ Scans the hosts and packages already installed locally on
         """ Scans the hosts and packages already installed locally on

+ 46 - 2
direct/src/p3d/HostInfo.py

@@ -40,8 +40,6 @@ class HostInfo:
         Note that perPlatform is also restricted by the individual
         Note that perPlatform is also restricted by the individual
         package's specification.  """
         package's specification.  """
 
 
-        assert appRunner or rootDir or hostDir
-
         self.__setHostUrl(hostUrl)
         self.__setHostUrl(hostUrl)
         self.appRunner = appRunner
         self.appRunner = appRunner
         self.rootDir = rootDir
         self.rootDir = rootDir
@@ -112,6 +110,52 @@ class HostInfo:
             # https-protected hostUrl, it will be the cleartext channel.
             # https-protected hostUrl, it will be the cleartext channel.
             self.downloadUrlPrefix = self.hostUrlPrefix
             self.downloadUrlPrefix = self.hostUrlPrefix
 
 
+    def freshenFile(self, http, fileSpec, localPathname):
+        """ Ensures that the localPathname is the most current version
+        of the file defined by fileSpec, as offered by host.  If not,
+        it downloads a new version on-the-spot.  Returns true on
+        success, false on failure. """
+
+        if fileSpec.quickVerify(pathname = localPathname):
+            # It's good, keep it.
+            return True
+
+        # It's stale, get a new one.
+        doc = None
+        if self.appRunner and self.appRunner.superMirrorUrl:
+            # Use the "super mirror" first.
+            url = core.URLSpec(self.appRunner.superMirrorUrl + fileSpec.filename)
+            self.notify.info("Freshening %s" % (url))
+            doc = http.getDocument(url)
+
+        if not doc or not doc.isValid():
+            # Failing the super mirror, contact the actual host.
+            url = core.URLSpec(self.hostUrlPrefix + fileSpec.filename)
+            self.notify.info("Freshening %s" % (url))
+            doc = http.getDocument(url)
+            if not doc.isValid():
+                return False
+
+        file = Filename.temporary('', 'p3d_')
+        if not doc.downloadToFile(file):
+            # Failed to download.
+            file.unlink()
+            return False
+
+        # Successfully downloaded!
+        localPathname.makeDir()
+        if not file.renameTo(localPathname):
+            # Couldn't move it into place.
+            file.unlink()
+            return False
+
+        if not fileSpec.fullVerify(pathname = localPathname, notify = self.notify):
+            # No good after download.
+            self.notify.info("%s is still no good after downloading." % (url))
+            return False
+
+        return True
+
     def downloadContentsFile(self, http, redownload = False,
     def downloadContentsFile(self, http, redownload = False,
                              hashVal = None):
                              hashVal = None):
         """ Downloads the contents.xml file for this particular host,
         """ Downloads the contents.xml file for this particular host,

+ 45 - 17
direct/src/p3d/Packager.py

@@ -13,12 +13,12 @@ import os
 import glob
 import glob
 import string
 import string
 import types
 import types
-import getpass
 import struct
 import struct
 import subprocess
 import subprocess
 import copy
 import copy
 from direct.p3d.FileSpec import FileSpec
 from direct.p3d.FileSpec import FileSpec
 from direct.p3d.SeqValue import SeqValue
 from direct.p3d.SeqValue import SeqValue
+from direct.p3d.HostInfo import HostInfo
 from direct.showbase import Loader
 from direct.showbase import Loader
 from direct.showbase import AppRunnerGlobal
 from direct.showbase import AppRunnerGlobal
 from direct.showutil import FreezeTool
 from direct.showutil import FreezeTool
@@ -378,7 +378,8 @@ class Packager:
             # This records the current list of modules we have added so
             # This records the current list of modules we have added so
             # far.
             # far.
             self.freezer = FreezeTool.Freezer(platform = self.packager.platform)
             self.freezer = FreezeTool.Freezer(platform = self.packager.platform)
-            
+            self.freezer.storePythonSource = self.packager.storePythonSource
+
             # Map of extensions to files to number (ignored by dir)
             # Map of extensions to files to number (ignored by dir)
             self.ignoredDirFiles = {}
             self.ignoredDirFiles = {}
 
 
@@ -1075,7 +1076,7 @@ class Packager:
             fpath.append(Filename("/Library/Frameworks"))
             fpath.append(Filename("/Library/Frameworks"))
             fpath.append(Filename("/System/Library/Frameworks"))
             fpath.append(Filename("/System/Library/Frameworks"))
             fpath.append(Filename("/Developer/Library/Frameworks"))
             fpath.append(Filename("/Developer/Library/Frameworks"))
-            fpath.append(Filename("/Users/%s" % getpass.getuser(), "Library/Frameworks"))
+            fpath.append(Filename(os.path.expanduser("~"), "Library/Frameworks"))
             if "HOME" in os.environ:
             if "HOME" in os.environ:
                 fpath.append(Filename(os.environ["HOME"], "Library/Frameworks"))
                 fpath.append(Filename(os.environ["HOME"], "Library/Frameworks"))
             ffilename = Filename(library.split('.framework/', 1)[0].split('/')[-1] + '.framework')
             ffilename = Filename(library.split('.framework/', 1)[0].split('/')[-1] + '.framework')
@@ -2233,6 +2234,11 @@ class Packager:
         self.host = PandaSystem.getPackageHostUrl()
         self.host = PandaSystem.getPackageHostUrl()
         self.addHost(self.host)
         self.addHost(self.host)
 
 
+        # This will be used when we're not compiling in the packaged
+        # environment.
+        self.__hostInfos = {}
+        self.http = HTTPClient.getGlobalPtr()
+
         # The maximum amount of time a client should cache the
         # The maximum amount of time a client should cache the
         # contents.xml before re-querying the server, in seconds.
         # contents.xml before re-querying the server, in seconds.
         self.maxAge = 0
         self.maxAge = 0
@@ -2318,6 +2324,10 @@ class Packager:
         # any applications.
         # any applications.
         self.allowPythonDev = False
         self.allowPythonDev = False
 
 
+        # Set this flag to store the original Python source files,
+        # without compiling them to .pyc or .pyo.
+        self.storePythonSource = False
+
         # Fill this with a list of (certificate, chain, pkey,
         # Fill this with a list of (certificate, chain, pkey,
         # password) tuples to automatically sign each p3d file
         # password) tuples to automatically sign each p3d file
         # generated.
         # generated.
@@ -3028,17 +3038,10 @@ class Packager:
 
 
     def __findPackageOnHost(self, packageName, platform, version, hostUrl, requires = None):
     def __findPackageOnHost(self, packageName, platform, version, hostUrl, requires = None):
         appRunner = AppRunnerGlobal.appRunner
         appRunner = AppRunnerGlobal.appRunner
-        if not appRunner:
-            # We don't download import files from a host unless we're
-            # running in a packaged environment ourselves.  It would
-            # be possible to do this, but a fair bit of work for not
-            # much gain--this is meant to be run in a packaged
-            # environment.
-            return None
 
 
         # Make sure we have a fresh version of the contents file.
         # Make sure we have a fresh version of the contents file.
-        host = appRunner.getHost(hostUrl)
-        if not host.downloadContentsFile(appRunner.http):
+        host = self.__getHostInfo(hostUrl)
+        if not host.downloadContentsFile(self.http):
             return None
             return None
 
 
         packageInfos = []
         packageInfos = []
@@ -3063,23 +3066,48 @@ class Packager:
 
 
             # Now we've retrieved a PackageInfo.  Get the import desc file
             # Now we've retrieved a PackageInfo.  Get the import desc file
             # from it.
             # from it.
-            filename = Filename(host.hostDir, 'imports/' + packageInfo.importDescFile.basename)
-            if not appRunner.freshenFile(host, packageInfo.importDescFile, filename):
+            if host.hostDir:
+                filename = Filename(host.hostDir, 'imports/' + packageInfo.importDescFile.basename)
+            else:
+                # We're not running in the packaged environment, so download
+                # to a temporary file instead of the host directory.
+                filename = Filename.temporary('', 'import_' + packageInfo.importDescFile.basename, '.xml')
+
+            if not host.freshenFile(self.http, packageInfo.importDescFile, filename):
                 self.notify.error("Couldn't download import file.")
                 self.notify.error("Couldn't download import file.")
                 continue
                 continue
 
 
             # Now that we have the import desc file, use it to load one of
             # Now that we have the import desc file, use it to load one of
             # our Package objects.
             # our Package objects.
             package = self.Package('', self)
             package = self.Package('', self)
-            if not package.readImportDescFile(filename):
-                continue
+            success = package.readImportDescFile(filename)
+
+            if not host.hostDir:
+                # Don't forget to delete the temporary file we created.
+                filename.unlink()
 
 
-            if self.__packageIsValid(package, requires, platform):
+            if success and self.__packageIsValid(package, requires, platform):
                 return package
                 return package
 
 
         # Couldn't find a suitable package.
         # Couldn't find a suitable package.
         return None
         return None
 
 
+    def __getHostInfo(self, hostUrl = None):
+        """ This shadows appRunner.getHost(), for the purpose of running
+        outside the packaged environment. """
+
+        if not hostUrl:
+            hostUrl = PandaSystem.getPackageHostUrl()
+
+        if AppRunnerGlobal.appRunner:
+            return AppRunnerGlobal.appRunner.getHost(hostUrl)
+
+        host = self.__hostInfos.get(hostUrl, None)
+        if not host:
+            host = HostInfo(hostUrl)
+            self.__hostInfos[hostUrl] = host
+        return host
+
     def __sortImportPackages(self, packages):
     def __sortImportPackages(self, packages):
         """ Given a list of Packages read from *.import.xml filenames,
         """ Given a list of Packages read from *.import.xml filenames,
         sorts them in reverse order by version, so that the
         sorts them in reverse order by version, so that the

+ 9 - 1
direct/src/p3d/ppackage.py

@@ -83,6 +83,10 @@ Options:
      initially, but should not be set on an application intended for
      initially, but should not be set on an application intended for
      deployment.
      deployment.
 
 
+  -N
+     If this option is set, Packager will not try to compile any Python
+     files to .pyc or .pyo, instead storing the original source files.
+
   -u
   -u
      On the Mac OSX platform, this means that Panda was built with
      On the Mac OSX platform, this means that Panda was built with
      universal binaries, and the package should be built that way as
      universal binaries, and the package should be built that way as
@@ -152,6 +156,7 @@ buildPatches = False
 installSearch = []
 installSearch = []
 signParams = []
 signParams = []
 allowPythonDev = False
 allowPythonDev = False
+storePythonSource = False
 universalBinaries = False
 universalBinaries = False
 systemRoot = None
 systemRoot = None
 ignoreSetHost = False
 ignoreSetHost = False
@@ -160,7 +165,7 @@ p3dSuffix = ''
 platforms = []
 platforms = []
 
 
 try:
 try:
-    opts, args = getopt.getopt(sys.argv[1:], 'i:ps:S:DuP:R:Ha:hv')
+    opts, args = getopt.getopt(sys.argv[1:], 'i:ps:S:DNuP:R:Ha:hv')
 except getopt.error as msg:
 except getopt.error as msg:
     usage(1, msg)
     usage(1, msg)
 
 
@@ -182,6 +187,8 @@ for opt, arg in opts:
                            Filename.fromOsSpecific(password)))
                            Filename.fromOsSpecific(password)))
     elif opt == '-D':
     elif opt == '-D':
         allowPythonDev = True
         allowPythonDev = True
+    elif opt == '-N':
+        storePythonSource = True
     elif opt == '-u':
     elif opt == '-u':
         universalBinaries = True
         universalBinaries = True
     elif opt == '-P':
     elif opt == '-P':
@@ -236,6 +243,7 @@ for platform in platforms:
         packager.installSearch = [installDir] + packager.installSearch
         packager.installSearch = [installDir] + packager.installSearch
     packager.signParams = signParams
     packager.signParams = signParams
     packager.allowPythonDev = allowPythonDev
     packager.allowPythonDev = allowPythonDev
+    packager.storePythonSource = storePythonSource
     packager.systemRoot = systemRoot
     packager.systemRoot = systemRoot
     packager.ignoreSetHost = ignoreSetHost
     packager.ignoreSetHost = ignoreSetHost
     packager.verbosePrint = verbosePrint
     packager.verbosePrint = verbosePrint

+ 4 - 1
direct/src/showbase/PythonUtil.py

@@ -325,7 +325,10 @@ def adjust(command = None, dim = 1, parent = None, **kw):
     10.0
     10.0
     """
     """
     # Make sure we enable Tk
     # Make sure we enable Tk
-    from direct.tkwidgets import Valuator
+    # Don't use a regular import, to prevent ModuleFinder from picking
+    # it up as a dependency when building a .p3d package.
+    import importlib
+    Valuator = importlib.import_module('direct.tkwidgets.Valuator')
     # Set command if specified
     # Set command if specified
     if command:
     if command:
         kw['command'] = lambda x: apply(command, x)
         kw['command'] = lambda x: apply(command, x)

+ 4 - 1
direct/src/showbase/ShowBaseGlobal.py

@@ -14,7 +14,10 @@ assert base
 directNotify.setDconfigLevels()
 directNotify.setDconfigLevels()
 
 
 def inspect(anObject):
 def inspect(anObject):
-    from direct.tkpanels import Inspector
+    # Don't use a regular import, to prevent ModuleFinder from picking
+    # it up as a dependency when building a .p3d package.
+    import importlib
+    Inspector = importlib.import_module('direct.tkpanels.Inspector')
     return Inspector.inspect(anObject)
     return Inspector.inspect(anObject)
 
 
 import __builtin__
 import __builtin__

+ 1 - 0
direct/src/showutil/FreezeTool.py

@@ -1143,6 +1143,7 @@ class Freezer:
         elif getattr(module, '__file__', None):
         elif getattr(module, '__file__', None):
             sourceFilename = Filename.fromOsSpecific(module.__file__)
             sourceFilename = Filename.fromOsSpecific(module.__file__)
             sourceFilename.setExtension("py")
             sourceFilename.setExtension("py")
+            sourceFilename.setText()
 
 
         if self.storePythonSource:
         if self.storePythonSource:
             if sourceFilename and sourceFilename.exists():
             if sourceFilename and sourceFilename.exists():

+ 4 - 1
direct/src/task/Task.py

@@ -582,7 +582,10 @@ class TaskManager:
         return numFound
         return numFound
 
 
     def popupControls(self):
     def popupControls(self):
-        from direct.tkpanels import TaskManagerPanel
+        # Don't use a regular import, to prevent ModuleFinder from picking
+        # it up as a dependency when building a .p3d package.
+        import importlib
+        TaskManagerPanel = importlib.import_module('direct.tkpanels.TaskManagerPanel')
         return TaskManagerPanel.TaskManagerPanel(self)
         return TaskManagerPanel.TaskManagerPanel(self)
 
 
     def getProfileSession(self, name=None):
     def getProfileSession(self, name=None):

+ 5 - 0
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -4179,6 +4179,11 @@ bool RemapCompareLess(FunctionRemap *in1, FunctionRemap *in2) {
   assert(in1 != NULL);
   assert(in1 != NULL);
   assert(in2 != NULL);
   assert(in2 != NULL);
 
 
+  if (in1->_const_method != in2->_const_method) {
+    // Non-const methods should come first.
+    return in2->_const_method;
+  }
+
   if (in1->_parameters.size() != in2->_parameters.size()) {
   if (in1->_parameters.size() != in2->_parameters.size()) {
     return (in1->_parameters.size() > in2->_parameters.size());
     return (in1->_parameters.size() > in2->_parameters.size());
   }
   }

+ 40 - 18
makepanda/makepanda.py

@@ -1856,6 +1856,11 @@ def Package(target, inputs, opts):
 
 
     command += "direct/src/p3d/ppackage.py"
     command += "direct/src/p3d/ppackage.py"
 
 
+    if not RTDIST:
+        # Don't compile Python sources, because we might not running in the same
+        # Python version as the selected host.
+        command += " -N"
+
     if GetTarget() == "darwin":
     if GetTarget() == "darwin":
         if SDK.get("MACOSX"):
         if SDK.get("MACOSX"):
             command += " -R \"%s\"" % SDK["MACOSX"]
             command += " -R \"%s\"" % SDK["MACOSX"]
@@ -1868,8 +1873,31 @@ def Package(target, inputs, opts):
     command += " -i \"" + GetOutputDir() + "/stage\""
     command += " -i \"" + GetOutputDir() + "/stage\""
     if (P3DSUFFIX):
     if (P3DSUFFIX):
         command += ' -a "' + P3DSUFFIX + '"'
         command += ' -a "' + P3DSUFFIX + '"'
+
     command += " " + inputs[0]
     command += " " + inputs[0]
-    oscmd(command)
+
+    if GetOrigExt(target) == '.p3d':
+        # Build a specific .p3d file.
+        basename = os.path.basename(os.path.splitext(target)[0])
+        command += " " + basename
+        oscmd(command)
+
+        if GetTarget() == 'windows':
+            # Make an .exe that calls this .p3d.
+            objfile = FindLocation('p3dWrapper_' + basename + '.obj', [])
+            CompileCxx(objfile, 'direct/src/p3d/p3dWrapper.c', [])
+
+            exefile = FindLocation(basename + '.exe', [])
+            CompileLink(exefile, [objfile], ['ADVAPI'])
+
+        # Move it to the bin directory.
+        os.rename(GetOutputDir() + '/stage/' + basename + P3DSUFFIX + '.p3d', target)
+
+        if sys.platform != 'win32':
+            oscmd('chmod +x ' + BracketNameWithQuotes(target))
+    else:
+        # This is presumably a package or set of packages.
+        oscmd(command)
 
 
 ##########################################################################################
 ##########################################################################################
 #
 #
@@ -1970,7 +1998,10 @@ def CompileAnything(target, inputs, opts, progress = None):
         ProgressOutput(progress, "Compiling MIDL file", infile)
         ProgressOutput(progress, "Compiling MIDL file", infile)
         return CompileMIDL(target, infile, opts)
         return CompileMIDL(target, infile, opts)
     elif (infile.endswith(".pdef")):
     elif (infile.endswith(".pdef")):
-        ProgressOutput(progress, "Building package from pdef file", infile)
+        if origsuffix == '.p3d':
+            ProgressOutput(progress, "Building package", target)
+        else:
+            ProgressOutput(progress, "Building package from pdef file", infile)
         return Package(target, inputs, opts)
         return Package(target, inputs, opts)
     elif origsuffix in SUFFIX_LIB:
     elif origsuffix in SUFFIX_LIB:
         ProgressOutput(progress, "Linking static library", target)
         ProgressOutput(progress, "Linking static library", target)
@@ -6234,26 +6265,17 @@ if (RTDIST):
   TargetAdd('_thirdparty', opts=OPTS, input='thirdparty.pdef')
   TargetAdd('_thirdparty', opts=OPTS, input='thirdparty.pdef')
 
 
 #
 #
-# Distribute prebuilt .p3d files as executable.
+# If we have a host URL and distributor, we can make .p3d deployment tools.
 #
 #
 
 
-if (PkgSkip("DIRECT")==0 and not RUNTIME and not RTDIST):
-  if GetTarget() == 'windows':
+if not PkgSkip("DIRECT") and not PkgSkip("DEPLOYTOOLS") and not RUNTIME and not RTDIST and HOST_URL and DISTRIBUTOR:
     OPTS=['DIR:direct/src/p3d']
     OPTS=['DIR:direct/src/p3d']
-    TargetAdd('p3dWrapper.obj', opts=OPTS, input='p3dWrapper.c')
-    TargetAdd('p3dWrapper.exe', input='p3dWrapper.obj')
-    TargetAdd('p3dWrapper.exe', opts=["ADVAPI"])
 
 
-  for g in glob.glob("direct/src/p3d/*.p3d"):
-    base = os.path.basename(g)
-    base = base.split(".", 1)[0]
-
-    if GetTarget() == 'windows':
-      TargetAdd(base+".exe", input='p3dWrapper.exe')
-      CopyFile(GetOutputDir()+"/bin/"+base+".p3d", g)
-    else:
-      CopyFile(GetOutputDir()+"/bin/"+base, g)
-      oscmd("chmod +x "+GetOutputDir()+"/bin/"+base)
+    TargetAdd('packp3d.p3d', opts=OPTS, input='panda3d.pdef')
+    TargetAdd('pdeploy.p3d', opts=OPTS, input='panda3d.pdef')
+    TargetAdd('pmerge.p3d', opts=OPTS, input='panda3d.pdef')
+    TargetAdd('ppackage.p3d', opts=OPTS, input='panda3d.pdef')
+    TargetAdd('ppatcher.p3d', opts=OPTS, input='panda3d.pdef')
 
 
 ##########################################################################################
 ##########################################################################################
 #
 #

+ 4 - 1
makepanda/makepandacore.py

@@ -1881,7 +1881,7 @@ def SdkLocatePython(prefer_thirdparty_python=False):
             sdkdir += "-x64"
             sdkdir += "-x64"
 
 
         SDK["PYTHON"] = sdkdir
         SDK["PYTHON"] = sdkdir
-        SDK["PYTHONEXEC"] = SDK["PYTHON"].replace('/', '\\') + "\\python"
+        SDK["PYTHONEXEC"] = SDK["PYTHON"].replace('\\', '/') + "/python"
         if (GetOptimize() <= 2):
         if (GetOptimize() <= 2):
             SDK["PYTHONEXEC"] += "_d.exe"
             SDK["PYTHONEXEC"] += "_d.exe"
         else:
         else:
@@ -2824,6 +2824,7 @@ def CalcLocation(fn, ipath):
         if (fn.endswith(".dle")):   return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".dle"
         if (fn.endswith(".dle")):   return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".dle"
         if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn[:-7]+dllext+".dll"
         if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn[:-7]+dllext+".dll"
         if (fn.endswith(".exe")):   return OUTPUTDIR+"/bin/"+fn
         if (fn.endswith(".exe")):   return OUTPUTDIR+"/bin/"+fn
+        if (fn.endswith(".p3d")):   return OUTPUTDIR+"/bin/"+fn
         if (fn.endswith(".lib")):   return OUTPUTDIR+"/lib/"+fn[:-4]+dllext+".lib"
         if (fn.endswith(".lib")):   return OUTPUTDIR+"/lib/"+fn[:-4]+dllext+".lib"
         if (fn.endswith(".ilb")):   return OUTPUTDIR+"/tmp/"+fn[:-4]+dllext+".lib"
         if (fn.endswith(".ilb")):   return OUTPUTDIR+"/tmp/"+fn[:-4]+dllext+".lib"
     elif (target == 'darwin'):
     elif (target == 'darwin'):
@@ -2835,6 +2836,7 @@ def CalcLocation(fn, ipath):
         if (fn.endswith(".pyd")):   return OUTPUTDIR+"/panda3d/"+fn[:-4]+".so"
         if (fn.endswith(".pyd")):   return OUTPUTDIR+"/panda3d/"+fn[:-4]+".so"
         if (fn.endswith(".mll")):   return OUTPUTDIR+"/plugins/"+fn
         if (fn.endswith(".mll")):   return OUTPUTDIR+"/plugins/"+fn
         if (fn.endswith(".exe")):   return OUTPUTDIR+"/bin/"+fn[:-4]
         if (fn.endswith(".exe")):   return OUTPUTDIR+"/bin/"+fn[:-4]
+        if (fn.endswith(".p3d")):   return OUTPUTDIR+"/bin/"+fn[:-4]
         if (fn.endswith(".lib")):   return OUTPUTDIR+"/lib/"+fn[:-4]+".a"
         if (fn.endswith(".lib")):   return OUTPUTDIR+"/lib/"+fn[:-4]+".a"
         if (fn.endswith(".ilb")):   return OUTPUTDIR+"/tmp/"+fn[:-4]+".a"
         if (fn.endswith(".ilb")):   return OUTPUTDIR+"/tmp/"+fn[:-4]+".a"
         if (fn.endswith(".rsrc")):  return OUTPUTDIR+"/tmp/"+fn
         if (fn.endswith(".rsrc")):  return OUTPUTDIR+"/tmp/"+fn
@@ -2857,6 +2859,7 @@ def CalcLocation(fn, ipath):
         if (fn.endswith(".mll")):   return OUTPUTDIR+"/plugins/"+fn
         if (fn.endswith(".mll")):   return OUTPUTDIR+"/plugins/"+fn
         if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn[:-7]+dllext+".so"
         if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn[:-7]+dllext+".so"
         if (fn.endswith(".exe")):   return OUTPUTDIR+"/bin/"+fn[:-4]
         if (fn.endswith(".exe")):   return OUTPUTDIR+"/bin/"+fn[:-4]
+        if (fn.endswith(".p3d")):   return OUTPUTDIR+"/bin/"+fn[:-4]
         if (fn.endswith(".lib")):   return OUTPUTDIR+"/lib/"+fn[:-4]+".a"
         if (fn.endswith(".lib")):   return OUTPUTDIR+"/lib/"+fn[:-4]+".a"
         if (fn.endswith(".ilb")):   return OUTPUTDIR+"/tmp/"+fn[:-4]+".a"
         if (fn.endswith(".ilb")):   return OUTPUTDIR+"/tmp/"+fn[:-4]+".a"
     if (fn.endswith(".dat")):   return OUTPUTDIR+"/tmp/"+fn
     if (fn.endswith(".dat")):   return OUTPUTDIR+"/tmp/"+fn