diff --git a/direct/src/configfiles/panda3d.pdef b/direct/src/configfiles/panda3d.pdef new file mode 100755 index 0000000000..e8035c5211 --- /dev/null +++ b/direct/src/configfiles/panda3d.pdef @@ -0,0 +1,166 @@ +# This file defines a number of standard "packages" that correspond to +# a Panda3D distribution. These packages are built by passing this +# file to the ppackage utility, either as a packaged application, or +# as the module direct.showutil.ppackage. + +# These packages are then downloaded by the Panda3D plugin and +# standalone runtime executable, and they contain the actual Panda3D +# runtime libraries. + +# We divide the runtime into several smaller packages. There is the +# core Panda3D package, which is needed by all Panda3D applications, +# and then a number of smaller, optional packages, which may or may +# not be needed by any one particular application. + + +begin_package panda3d + +# The core Panda3D package. Contains Python and most of the graphics +# code in Panda3D. + +config display_name="Panda3D" + +# This is the key Python module that is imported at runtime to start +# an application running. +module direct.showutil.runp3d + +# These are additional Python modules that are needed by most Panda3D +# applications. It doesn't matter too much if we miss one or two +# here, since any module imported by any of this code will +# automatically be included as well, and we end up with a pretty +# complete list that way. +module direct.directbase.DirectStart +module direct.showbase.* +module direct.actor.Actor +module direct.fsm.FSM +module direct.interval.IntervalGlobal +module direct.particles.ParticleEffect +module direct.directutil.Mopath + +# Exclude these GUI toolkits; they're big, and many applications don't +# use them. We define them as separate, optional packages, below. +exclude_module wx +exclude_module direct.showbase.WxGlobal + +exclude_module Tkinter +exclude_module direct.showbase.TkGlobal +exclude_module direct.tkpanels +exclude_module direct.tkwidgets + +# Bind all of the above Python code into a frozen DLL. This makes the +# Python code available when the DLL is imported. It is actually +# preferable not to use freeze, but instead just to leave the Python +# code directly within the Multifile; but in this case we have to use +# freeze on this very first package, due to bootstrapping +# requirements. (Part of the code we're including here is the code +# required to load Python code from a Multifile, so it can't be placed +# within a Multifile itself.) +freeze_dll runp3d_frozen + +# This is the main program that drives the plugin application. It is +# responsible for loading runp3d_frozen, above, and then importing +# direct.showutil.runp3d, to start an application running. Note that +# the .exe extension is automatically replaced with the +# platform-specific extension appropriate for an executable. +file p3dpython.exe + +# Most of the core Panda3D DLL's will be included implicitly due to +# being referenced by the above Python code. Here we name a few more +# that are also needed, but aren't referenced by any code. Again, +# note that the .dll extension is automatically replaced with the +# platform-specific extension for an executable. +file libpandagl.dll +file libpandadx8.dll +file libpandadx9.dll +file libtinydisplay.dll + +# A basic config file is needed to lay some some fundamental runtime +# variables. +inline_file Config.prc extract=1 <<- + plugin-path $PANDA3D_ROOT + aux-display pandagl + aux-display pandadx9 + aux-display pandadx8 + aux-display tinydisplay + + +end_package panda3d + + +begin_package egg + +# This package contains the code for reading and operating on egg +# files. Since the Packager automatically converts egg files to bam +# files, this is not needed for most Panda3D applications. + +config display_name="Panda3D egg loader" +require panda3d + +file libpandaegg.dll + +inline_file egg.prc extract=1 <<- + plugin-path $EGG_ROOT + load-file-type egg pandaegg + + +end_package egg + + +begin_package wx +config display_name="wxPython GUI Toolkit" +require panda3d + +module direct.showbase.WxGlobal +module wx +module wx.* + +end_package wx + + + +begin_package tk +config display_name="Tk GUI Toolkit" +require panda3d + +module Tkinter +module direct.showbase.TkGlobal +module direct.tkpanels +module direct.tkwidgets + +end_package tk + + +begin_p3d packp3d + +# This application is a command-line convenience for building a p3d +# application out of a directory hierarchy on disk. We build it here +# into its own p3d application, to allow end-users to easily build p3d +# applications using the appropriate version of Python and Panda for +# the targeted runtime. + +config display_name="Panda3D Application Packer" +config full_disk_access=1 +config hidden=1 +require panda3d +require egg + +main_module direct.showutil.packp3d + +end_p3d packp3d + + +begin_p3d ppackage + +# As above, a packaging utility. This is the fully-general ppackage +# utility, which reads pdef files (like this one!) and creates one or +# more packages or p3d applications. + +config display_name="Panda3D General Package Utility" +config full_disk_access=1 +config hidden=1 +require panda3d +require egg + +main_module direct.showutil.ppackage + +end_p3d ppackage diff --git a/direct/src/showutil/Packager.py b/direct/src/showutil/Packager.py index 7e32fbe550..82890d2c91 100644 --- a/direct/src/showutil/Packager.py +++ b/direct/src/showutil/Packager.py @@ -252,7 +252,7 @@ class Packager: xmodule.SetAttribute('forbid', '1') if mdef.exclude and mdef.allowChildren: xmodule.SetAttribute('allowChildren', '1') - self.components.append(xmodule) + self.components.append((newName.lower(), xmodule)) # Now look for implicit shared-library dependencies. if PandaSystem.getPlatform().startswith('win'): @@ -727,7 +727,8 @@ class Packager: xpackage.InsertEndChild(xuncompressedArchive) xpackage.InsertEndChild(xcompressedArchive) - for xextract in self.extracts: + self.extracts.sort() + for name, xextract in self.extracts: xpackage.InsertEndChild(xextract) doc.InsertEndChild(xpackage) @@ -755,7 +756,8 @@ class Packager: xrequires.SetAttribute('version', package.version) xpackage.InsertEndChild(xrequires) - for xcomponent in self.components: + self.components.sort() + for name, xcomponent in self.components: xpackage.InsertEndChild(xcomponent) doc.InsertEndChild(xpackage) @@ -933,7 +935,7 @@ class Packager: xcomponent = TiXmlElement('component') xcomponent.SetAttribute('filename', newName) - self.components.append(xcomponent) + self.components.append((newName.lower(), xcomponent)) def addFoundTexture(self, filename): """ Adds the newly-discovered texture to the output, if it has @@ -973,11 +975,11 @@ class Packager: self.multifile.addSubfile(file.newName, file.filename, compressionLevel) if file.extract: xextract = self.getFileSpec('extract', file.filename, file.newName) - self.extracts.append(xextract) + self.extracts.append((file.newName.lower(), xextract)) xcomponent = TiXmlElement('component') xcomponent.SetAttribute('filename', file.newName) - self.components.append(xcomponent) + self.components.append((file.newName.lower(), xcomponent)) def requirePackage(self, package): """ Indicates a dependency on the given package. This @@ -1076,7 +1078,29 @@ class Packager: self.binaryExtensions = [ 'ttf', 'wav', 'mid' ] # Files that represent an executable or shared library. - self.executableExtensions = [ 'dll', 'pyd', 'so', 'dylib', 'exe' ] + if self.platform.startswith('win'): + self.executableExtensions = [ 'dll', 'pyd', 'exe' ] + elif self.platform.startswith('osx'): + self.executableExtensions = [ 'so', 'dylib' ] + else: + self.executableExtensions = [ 'so' ] + + # Extensions that are automatically remapped by convention. + self.remapExtensions = {} + if self.platform.startswith('win'): + pass + elif self.platform.startswith('osx'): + self.remapExtensions = { + 'dll' : 'dylib', + 'pyd' : 'dylib', + 'exe' : '' + } + else: + self.remapExtensions = { + 'dll' : 'so', + 'pyd' : 'so', + 'exe' : '' + } # Files that should be extracted to disk. self.extractExtensions = self.executableExtensions[:] @@ -1596,10 +1620,10 @@ class Packager: def parse_file(self, words): """ - file filename [newNameOrDir] [extract=1] [executable=1] + file filename [newNameOrDir] [extract=1] [executable=1] [literal=1] """ - args = self.__parseArgs(words, ['extract', 'executable']) + args = self.__parseArgs(words, ['extract', 'executable', 'literal']) newNameOrDir = None @@ -1619,9 +1643,13 @@ class Packager: if executable is not None: executable = int(executable) + literal = args.get('literal', None) + if literal is not None: + literal = int(literal) + self.file(Filename.fromOsSpecific(filename), newNameOrDir = newNameOrDir, extract = extract, - executable = executable) + executable = executable, literal = literal) def parse_inline_file(self, words): """ @@ -2133,7 +2161,8 @@ class Packager: package.mainModule = None def file(self, filename, source = None, newNameOrDir = None, - extract = None, executable = None, deleteTemp = False): + extract = None, executable = None, deleteTemp = False, + literal = False): """ Adds the indicated arbitrary file to the current package. The file is placed in the named directory, or the toplevel @@ -2162,6 +2191,12 @@ class Packager: If deleteTemp is true, the file is a temporary file and will be deleted after its contents are copied to the package. + + If literal is true, then the file extension will be respected + exactly as it appears, and glob characters will not be + expanded. If this is false, then .dll or .exe files will be + renamed to .dylib and no extension on OSX (or .so on Linux); + and glob characters will be expanded. """ @@ -2169,9 +2204,26 @@ class Packager: raise OutsideOfPackageError filename = Filename(filename) - files = glob.glob(filename.toOsSpecific()) - if not files: + + if literal: files = [filename.toOsSpecific()] + + else: + ext = filename.getExtension() + + # A special case, since OSX and Linux don't have a + # standard extension for program files. + if executable is None and ext == 'exe': + executable = True + + newExt = self.remapExtensions.get(ext, None) + if newExt is not None: + filename.setExtension(newExt) + + files = glob.glob(filename.toOsSpecific()) + if not files: + files = [filename.toOsSpecific()] + explicit = (len(files) == 1) newName = None diff --git a/direct/src/showutil/ppackage.py b/direct/src/showutil/ppackage.py index 9f9c3aba3a..a42be43610 100755 --- a/direct/src/showutil/ppackage.py +++ b/direct/src/showutil/ppackage.py @@ -1,15 +1,22 @@ #! /usr/bin/env python """ -This script can be used to produce a downloadable "Package", which may -contain arbitrary files--for instance, Python code, bam files, and/or -compiled DLL's--and which may be downloaded by application code to -extend an application at runtime. +This script can be used to produce a Panda3D downloadable "package", +which may contain arbitrary files--for instance, Python code, bam +files, and/or compiled DLL's--and which may be downloaded by +application code to extend an application at runtime. -In addition to building the package in the first place, this script -can also be used to 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. +In addition to packages, this script can also be used to build +standalone p3d applications, that is, packaged Python code in a p3d +file, for execution by the Panda3D plugin or runtime. (But also see +packp3d, which is designed to be a simpler interface for building +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. diff --git a/direct/src/showutil/runp3d.py b/direct/src/showutil/runp3d.py index 089f95922d..6f7807c9a9 100755 --- a/direct/src/showutil/runp3d.py +++ b/direct/src/showutil/runp3d.py @@ -222,8 +222,11 @@ class AppRunner(DirectObject): mainName = self.p3dPackage.Attribute('main_module') if mainName: moduleName = mainName - - v = VFSImporter.VFSImporter(MultifileRoot) + + root = MultifileRoot + if '.' in moduleName: + root += '/' + '/'.join(moduleName.split('.')[:-1]) + v = VFSImporter.VFSImporter(root) loader = v.find_module(moduleName) if not loader: message = "No %s found in application." % (moduleName) @@ -264,6 +267,10 @@ class AppRunner(DirectObject): self.tokenDict = dict(tokens) self.argv = argv + # Also store the arguments on sys, for applications that + # aren't instance-ready. + sys.argv = argv + # Tell the browser that Python is up and running, and ready to # respond to queries. self.notifyRequest('onpythonload')