Browse Source

More pdeploy stuff.

rdb 16 years ago
parent
commit
a737a35314
3 changed files with 94 additions and 52 deletions
  1. 87 45
      direct/src/p3d/DeploymentTools.py
  2. 1 1
      direct/src/p3d/coreapi.pdef
  3. 6 6
      direct/src/p3d/pdeploy.py

+ 87 - 45
direct/src/p3d/DeploymentTools.py

@@ -9,6 +9,7 @@ from direct.directnotify.DirectNotifyGlobal import *
 from pandac.PandaModules import PandaSystem, HTTPClient, Filename, VirtualFileSystem
 from pandac.PandaModules import PandaSystem, HTTPClient, Filename, VirtualFileSystem
 from direct.p3d.HostInfo import HostInfo
 from direct.p3d.HostInfo import HostInfo
 from direct.showbase.AppRunnerGlobal import appRunner
 from direct.showbase.AppRunnerGlobal import appRunner
+import glob
 
 
 class CachedFile:
 class CachedFile:
     def __init__(self): self.str = ""
     def __init__(self): self.str = ""
@@ -48,6 +49,8 @@ class Standalone:
         if len(platforms) == 0:
         if len(platforms) == 0:
             Standalone.notify.error("No platforms found to build for!")
             Standalone.notify.error("No platforms found to build for!")
         
         
+        outputDir = Filename(outputDir + "/")
+        outputDir.makeDir()
         for platform in platforms:
         for platform in platforms:
             if platform.startswith("win"):
             if platform.startswith("win"):
                 self.build(Filename(outputDir, platform + "/" + self.basename + ".exe"), platform)
                 self.build(Filename(outputDir, platform + "/" + self.basename + ".exe"), platform)
@@ -82,8 +85,7 @@ class Standalone:
                 Standalone.notify.error("  -> %s failed for platform %s" % (package.packageName, package.platform))
                 Standalone.notify.error("  -> %s failed for platform %s" % (package.packageName, package.platform))
                 continue
                 continue
             
             
-            self.embed(output, p3dembed)
-            return
+            return self.embed(output, p3dembed)
         
         
         Standalone.notify.error("Failed to build standalone for platform %s" % platform)
         Standalone.notify.error("Failed to build standalone for platform %s" % platform)
     
     
@@ -123,6 +125,28 @@ class Standalone:
         
         
         os.chmod(output.toOsSpecific(), 0755)
         os.chmod(output.toOsSpecific(), 0755)
 
 
+    def getExtraFiles(self, platform):
+        """ Returns a list of extra files that will need to be included
+        with the standalone executable in order for it to run, such as
+        dependent libraries. The returned paths will be absolute. """
+        
+        for package in self.host.getPackages(name = "p3dembed", platform = platform):
+            if not package.downloadDescFile(self.http):
+                Standalone.notify.error("  -> %s failed for platform %s" % (package.packageName, package.platform))
+                continue
+            if not package.downloadPackage(self.http):
+                Standalone.notify.error("  -> %s failed for platform %s" % (package.packageName, package.platform))
+                continue
+            
+            directory = Filename(self.host.hostDir, "p3dembed/%s/p3dembed.exe" % package.platform)
+            directory.makeAbsolute()
+            filelist = []
+            for f in glob.glob(os.path.join(directory.toOsSpecific(), "*")):
+                if not f.endswith("p3dembed") and not f.endswith("p3dembed.exe"):
+                    filelist.append(Filename.fromOsSpecific(f))
+            return filelist
+        return []
+
 class Installer:
 class Installer:
     """ This class creates a (graphical) installer from a given .p3d file. """
     """ This class creates a (graphical) installer from a given .p3d file. """
     notify = directNotify.newCategory("Installer")
     notify = directNotify.newCategory("Installer")
@@ -142,15 +166,17 @@ class Installer:
         Call this after you have set the desired parameters. """
         Call this after you have set the desired parameters. """
         
         
         platforms = set()
         platforms = set()
-        for package in self.host.getPackages(name = "p3dembed"):
+        for package in self.standalone.host.getPackages(name = "p3dembed"):
             platforms.add(package.platform)
             platforms.add(package.platform)
         if len(platforms) == 0:
         if len(platforms) == 0:
             Installer.notify.error("No platforms found to build for!")
             Installer.notify.error("No platforms found to build for!")
         
         
-        outputDir = Filename(outputDir)
-        assert outputDir.isDirectory()
+        outputDir = Filename(outputDir + "/")
+        outputDir.makeDir()
         for platform in platforms:
         for platform in platforms:
-            self.build(outputDir, platform)
+            output = Filename(outputDir, platform + "/")
+            output.makeDir()
+            self.build(output, platform)
     
     
     def build(self, output, platform = None):
     def build(self, output, platform = None):
         """ Builds a (graphical) installer and stores it into the path
         """ Builds a (graphical) installer and stores it into the path
@@ -162,21 +188,24 @@ class Installer:
             platform = PandaSystem.getPlatform()
             platform = PandaSystem.getPlatform()
         
         
         if platform == "win32":
         if platform == "win32":
-            return self.buildNSIS()
+            return self.buildNSIS(output, platform)
         elif "_" in platform:
         elif "_" in platform:
-            os, arch = platform.rsplit("_", 1)
+            os, arch = platform.split("_", 1)
             if os == "linux":
             if os == "linux":
-                return self.buildDEB(output, arch)
+                return self.buildDEB(output, platform)
             elif os == "osx":
             elif os == "osx":
-                return self.buildDMG(output, arch)
+                return self.buildPKG(output, platform)
+            elif os == "freebsd":
+                return self.buildDEB(output, platform)
         Installer.notify.info("Ignoring unknown platform " + platform)
         Installer.notify.info("Ignoring unknown platform " + platform)
 
 
-    def buildDEB(self, output, arch):
+    def buildDEB(self, output, platform):
         """ Builds a .deb archive and stores it in the path indicated
         """ Builds a .deb archive and stores it in the path indicated
         by the 'output' argument. It will be built for the architecture
         by the 'output' argument. It will be built for the architecture
         specified by the 'arch' argument.
         specified by the 'arch' argument.
         If 'output' is a directory, the deb file will be stored in it. """
         If 'output' is a directory, the deb file will be stored in it. """
         
         
+        arch = platform.rsplit("_", 1)[-1]
         output = Filename(output)
         output = Filename(output)
         if output.isDirectory():
         if output.isDirectory():
             output = Filename(output, "%s_%s_%s.deb" % (self.shortname.lower(), self.version, arch))
             output = Filename(output, "%s_%s_%s.deb" % (self.shortname.lower(), self.version, arch))
@@ -194,7 +223,7 @@ class Installer:
         controlfile.write("Description: %s\n" % self.fullname)
         controlfile.write("Description: %s\n" % self.fullname)
         controlfile.close()
         controlfile.close()
         Filename(tempdir, "usr/bin/").makeDir()
         Filename(tempdir, "usr/bin/").makeDir()
-        self.standalone.build(Filename(tempdir, "usr/bin/" + self.shortname.lower()), "linux_" + arch)
+        self.standalone.build(Filename(tempdir, "usr/bin/" + self.shortname.lower()), platform)
         if not self.licensefile.empty():
         if not self.licensefile.empty():
             Filename(tempdir, "usr/share/doc/%s/" % self.shortname.lower()).makeDir()
             Filename(tempdir, "usr/share/doc/%s/" % self.shortname.lower()).makeDir()
             shutil.copyfile(self.licensefile.toOsSpecific(), Filename(tempdir, "usr/share/doc/%s/copyright" % self.shortname.lower()).toOsSpecific())
             shutil.copyfile(self.licensefile.toOsSpecific(), Filename(tempdir, "usr/share/doc/%s/copyright" % self.shortname.lower()).toOsSpecific())
@@ -231,33 +260,22 @@ class Installer:
         debfile.close()
         debfile.close()
         shutil.rmtree(tempdir.toOsSpecific())
         shutil.rmtree(tempdir.toOsSpecific())
 
 
-    def __buildAPP(self, plugin_standalone):
-        pkgfn = "%s %s.pkg" % (self.shortname, self.version)
-        appname = "/Applications/%s.app" % self.longname
-        Installer.notify.info("Creating %s..." % pkgfn)
+    def buildAPP(self, output, platform):
         
         
-        # Create a temporary directory to hold the application in
-        tempdir = Filename.temporary("", self.shortname.lower() + "_app_", "") + "/"
-        tempdir = tempdir.toOsSpecific()
-        if os.path.exists(tempdir):
-            shutil.rmtree(tempdir)
-        os.makedirs(tempdir)
-        contents = os.path.join(tempdir, appname.lstrip("/"), "Contents")
-        os.makedirs(os.path.join(contents, "MacOS"))
-        os.makedirs(os.path.join(contents, "Resources"))
+        output = Filename(output)
+        if output.isDirectory() and output.getExtension() != 'app':
+            output = Filename(output, "%s.app" % self.fullname)
+        Installer.notify.info("Creating %s..." % output)
         
         
-        # Create the "launch" script used to run the game.
-        launch = open(os.path.join(contents, "MacOS", "launch"), "w")
-        print >>launch, '#!/bin/sh'
-        print >>launch, 'panda3d_mac ../Resources/%s' % self.p3dfile.getBasename()
-        launch.close()
-        shutil.copyfile(self.p3dfile.toOsSpecific(), os.path.join(contents, "Resources", self.p3dfile.getBasename()))
-        shutil.copyfile(target, os.path.join(contents, "MacOS", "panda3d_mac"))
+        # Create the executable for the application bundle
+        exefile = Filename(output, "Contents/MacOS/" + self.shortname)
+        exefile.makeDir()
+        self.standalone.build(exefile, platform)
         
         
         # Create the application plist file.
         # Create the application plist file.
         # Although it might make more sense to use Python's plistlib module here,
         # Although it might make more sense to use Python's plistlib module here,
         # it is not available on non-OSX systems before Python 2.6.
         # it is not available on non-OSX systems before Python 2.6.
-        plist = open(os.path.join(tempdir, appname.lstrip("/"), "Contents", "Info.plist"), "w")
+        plist = open(Filename(output, "Contents/Info.plist").toOsSpecific(), "w")
         print >>plist, '<?xml version="1.0" encoding="UTF-8"?>'
         print >>plist, '<?xml version="1.0" encoding="UTF-8"?>'
         print >>plist, '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
         print >>plist, '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
         print >>plist, '<plist version="1.0">'
         print >>plist, '<plist version="1.0">'
@@ -267,7 +285,7 @@ class Installer:
         print >>plist, '\t<key>CFBundleDisplayName</key>'
         print >>plist, '\t<key>CFBundleDisplayName</key>'
         print >>plist, '\t<string>%s</string>' % self.fullname
         print >>plist, '\t<string>%s</string>' % self.fullname
         print >>plist, '\t<key>CFBundleExecutable</key>'
         print >>plist, '\t<key>CFBundleExecutable</key>'
-        print >>plist, '\t<string>launch</string>'
+        print >>plist, '\t<string>%s</string>' % exefile.getBasename()
         print >>plist, '\t<key>CFBundleIdentifier</key>'
         print >>plist, '\t<key>CFBundleIdentifier</key>'
         print >>plist, '\t<string>%s.%s</string>' % (self.authorid, self.shortname)
         print >>plist, '\t<string>%s.%s</string>' % (self.authorid, self.shortname)
         print >>plist, '\t<key>CFBundleInfoDictionaryVersion</key>'
         print >>plist, '\t<key>CFBundleInfoDictionaryVersion</key>'
@@ -290,7 +308,15 @@ class Installer:
         print >>plist, '</plist>'
         print >>plist, '</plist>'
         plist.close()
         plist.close()
 
 
-    def buildNSIS(self):
+    def buildPKG(self, output, platform):
+        output = Filename(output)
+        #if output.isDirectory():
+        #    output = Filename(output, "%s %s.pkg" % (self.fullname, self.version))
+        
+        self.buildAPP(output, platform)
+        return
+
+    def buildNSIS(self, output, platform):
         # Check if we have makensis first
         # Check if we have makensis first
         makensis = None
         makensis = None
         if (sys.platform.startswith("win")):
         if (sys.platform.startswith("win")):
@@ -308,16 +334,26 @@ class Installer:
                     makensis = os.path.join(p, "makensis")
                     makensis = os.path.join(p, "makensis")
         
         
         if makensis == None:
         if makensis == None:
-            Installer.notify.warning("Makensis utility not found, no Windows installer will be built!")
+            Installer.notify.error("Makensis utility not found, no Windows installer will be built!")
             return
             return
-        Installer.notify.info("Creating %s.exe..." % self.shortname)
+        
+        output = Filename(output)
+        if output.isDirectory():
+            output = Filename(output, "%s %s.exe" % (self.fullname, self.version))
+        Installer.notify.info("Creating %s..." % output)
+        output.makeAbsolute()
+
+        exefile = Filename(Filename.getTempDirectory(), self.shortname + ".exe")
+        exefile.unlink()
+        self.standalone.build(exefile, platform)
 
 
-        tempfile = self.shortname + ".nsi"
-        nsi = open(tempfile, "w")
+        nsifile = Filename(Filename.getTempDirectory(), self.shortname + ".nsi")
+        nsifile.unlink()
+        nsi = open(nsifile.toOsSpecific(), "w")
 
 
         # Some global info
         # Some global info
         nsi.write('Name "%s"\n' % self.fullname)
         nsi.write('Name "%s"\n' % self.fullname)
-        nsi.write('OutFile "%s.exe"\n' % self.shortname)
+        nsi.write('OutFile "%s"\n' % output.toOsSpecific())
         nsi.write('InstallDir "$PROGRAMFILES\%s"\n' % self.fullname)
         nsi.write('InstallDir "$PROGRAMFILES\%s"\n' % self.fullname)
         nsi.write('SetCompress auto\n')
         nsi.write('SetCompress auto\n')
         nsi.write('SetCompressor lzma\n')
         nsi.write('SetCompressor lzma\n')
@@ -329,7 +365,7 @@ class Installer:
         nsi.write('RequestExecutionLevel admin\n')
         nsi.write('RequestExecutionLevel admin\n')
         nsi.write('\n')
         nsi.write('\n')
         nsi.write('Function launch\n')
         nsi.write('Function launch\n')
-        nsi.write('  ExecShell "open" "$INSTDIR\%s.bat"\n' % self.shortname)
+        nsi.write('  ExecShell "open" "$INSTDIR\%s.exe"\n' % self.shortname)
         nsi.write('FunctionEnd\n')
         nsi.write('FunctionEnd\n')
         nsi.write('\n')
         nsi.write('\n')
         nsi.write('!include "MUI2.nsh"\n')
         nsi.write('!include "MUI2.nsh"\n')
@@ -353,8 +389,11 @@ class Installer:
         nsi.write('!insertmacro MUI_LANGUAGE "English"\n')
         nsi.write('!insertmacro MUI_LANGUAGE "English"\n')
 
 
         # This section defines the installer.
         # This section defines the installer.
-        nsi.write('Section "Install"\n')
+        nsi.write('Section "" SecCore\n')
         nsi.write('  SetOutPath "$INSTDIR"\n')
         nsi.write('  SetOutPath "$INSTDIR"\n')
+        nsi.write('  File "%s"\n' % exefile.toOsSpecific())
+        for f in self.standalone.getExtraFiles(platform):
+            nsi.write('  File "%s"\n' % f.toOsSpecific())
         nsi.write('  WriteUninstaller "$INSTDIR\Uninstall.exe"\n')
         nsi.write('  WriteUninstaller "$INSTDIR\Uninstall.exe"\n')
         nsi.write('  ; Start menu items\n')
         nsi.write('  ; Start menu items\n')
         nsi.write('  !insertmacro MUI_STARTMENU_WRITE_BEGIN Application\n')
         nsi.write('  !insertmacro MUI_STARTMENU_WRITE_BEGIN Application\n')
@@ -364,7 +403,10 @@ class Installer:
         nsi.write('SectionEnd\n')
         nsi.write('SectionEnd\n')
 
 
         # This section defines the uninstaller.
         # This section defines the uninstaller.
-        nsi.write('Section "Uninstall"\n')
+        nsi.write('Section Uninstall\n')
+        nsi.write('  Delete "$INSTDIR\%s.exe"\n' % self.shortname)
+        for f in self.standalone.getExtraFiles(platform):
+            nsi.write('  Delete "%s"\n' % f.getBasename())
         nsi.write('  Delete "$INSTDIR\Uninstall.exe"\n')
         nsi.write('  Delete "$INSTDIR\Uninstall.exe"\n')
         nsi.write('  RMDir "$INSTDIR"\n')
         nsi.write('  RMDir "$INSTDIR"\n')
         nsi.write('  ; Start menu items\n')
         nsi.write('  ; Start menu items\n')
@@ -381,7 +423,7 @@ class Installer:
                 cmd += " /" + o
                 cmd += " /" + o
             else:
             else:
                 cmd += " -" + o
                 cmd += " -" + o
-        cmd += " " + tempfile
+        cmd += " " + nsifile.toOsSpecific()
         os.system(cmd)
         os.system(cmd)
 
 
-        os.remove(tempfile)
+        nsifile.unlink()

+ 1 - 1
direct/src/p3d/coreapi.pdef

@@ -99,7 +99,7 @@ class p3dcert(package):
         file('p3dcert.exe')
         file('p3dcert.exe')
 
 
 
 
-class p3dembed(solo):
+class p3dembed(package):
     # This contains the p3dembed executable, that is used by
     # This contains the p3dembed executable, that is used by
     # pdeploy to generate a self-extracting .p3d executable.
     # pdeploy to generate a self-extracting .p3d executable.
 
 

+ 6 - 6
direct/src/p3d/pdeploy.py

@@ -31,7 +31,7 @@ Modes:
 Options:
 Options:
 
 
   -n your_app
   -n your_app
-     Short, lowercase name of the application or game. Can only
+     Short, lowercase name of the application or game. May only
      contain alphanumeric characters, underscore or dash. This
      contain alphanumeric characters, underscore or dash. This
      name will also define the output file(s) of the process.
      name will also define the output file(s) of the process.
      If omitted, the basename of the p3d file is used.
      If omitted, the basename of the p3d file is used.
@@ -94,7 +94,7 @@ DEPLOY_MODES = ["standalone", "installer", "html"]
 import sys
 import sys
 import os
 import os
 import getopt
 import getopt
-from DeploymentTools import Standalone, Installer
+from direct.p3d.DeploymentTools import Standalone, Installer
 from pandac.PandaModules import Filename, PandaSystem
 from pandac.PandaModules import Filename, PandaSystem
 
 
 def usage(code, msg = ''):
 def usage(code, msg = ''):
@@ -208,10 +208,10 @@ elif deploy_mode == 'installer':
         i.buildAll(outputDir)
         i.buildAll(outputDir)
     else:
     else:
         for platform in platforms:
         for platform in platforms:
-            if platform.startswith("win"):
-                i.build(Filename(outputDir, platform + "/"), platform)
-            else:
-                i.build(Filename(outputDir, platform + "/"), platform)
+            output = Filename(outputDir, platform + "/")
+            output.makeDir()
+            i.build(output, platform)
+
 elif deploy_mode == 'html':
 elif deploy_mode == 'html':
     print "Creating %s.html..." % shortname
     print "Creating %s.html..." % shortname
     html = open(shortname + ".html", "w")
     html = open(shortname + ".html", "w")