mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
pdeploy is working for standalone executables now
This commit is contained in:
parent
2b0acc9fcc
commit
b9821f1df8
399
direct/src/p3d/DeploymentTools.py
Normal file
399
direct/src/p3d/DeploymentTools.py
Normal file
@ -0,0 +1,399 @@
|
|||||||
|
""" This module is used to build a graphical installer
|
||||||
|
or a standalone executable from a p3d file. It will try
|
||||||
|
to build for as many platforms as possible. """
|
||||||
|
|
||||||
|
__all__ = ["Standalone", "Installer"]
|
||||||
|
|
||||||
|
import os, sys, subprocess, tarfile, shutil, time
|
||||||
|
from direct.directnotify.DirectNotifyGlobal import *
|
||||||
|
from pandac.PandaModules import PandaSystem, HTTPClient, Filename, VirtualFileSystem
|
||||||
|
from direct.p3d.HostInfo import HostInfo
|
||||||
|
from direct.showbase.AppRunnerGlobal import appRunner
|
||||||
|
|
||||||
|
class CachedFile:
|
||||||
|
def __init__(self): self.str = ""
|
||||||
|
def write(self, data): self.str += data
|
||||||
|
|
||||||
|
# Make sure this matches with the magic in p3dEmbed.cxx.
|
||||||
|
P3DEMBED_MAGIC = "\xFF\x3D\x3D\x00"
|
||||||
|
|
||||||
|
class Standalone:
|
||||||
|
""" This class creates a standalone executable from a given .p3d file. """
|
||||||
|
notify = directNotify.newCategory("Standalone")
|
||||||
|
|
||||||
|
def __init__(self, p3dfile, tokens = {}):
|
||||||
|
if isinstance(p3dfile, Filename):
|
||||||
|
self.p3dfile = p3dfile
|
||||||
|
else:
|
||||||
|
self.p3dfile = Filename(p3dfile)
|
||||||
|
self.basename = self.p3dfile.getBasenameWoExtension()
|
||||||
|
self.tokens = tokens
|
||||||
|
|
||||||
|
if appRunner:
|
||||||
|
self.host = appRunner.getHost("http://runtime.panda3d.org")
|
||||||
|
else:
|
||||||
|
hostDir = Filename(Filename.getTempDirectory(), 'pdeploy/')
|
||||||
|
hostDir.makeDir()
|
||||||
|
self.host = HostInfo("http://runtime.panda3d.org", hostDir = hostDir, asMirror = True)
|
||||||
|
|
||||||
|
self.http = HTTPClient.getGlobalPtr()
|
||||||
|
if not self.host.downloadContentsFile(self.http):
|
||||||
|
Standalone.notify.error("couldn't read host")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def buildAll(self, outputDir = "."):
|
||||||
|
""" Builds standalone executables for every known platform,
|
||||||
|
into the specified output directory. """
|
||||||
|
|
||||||
|
platforms = set()
|
||||||
|
for package in self.host.getPackages(name = "p3dembed"):
|
||||||
|
platforms.add(package.platform)
|
||||||
|
if len(platforms) == 0:
|
||||||
|
Standalone.notify.error("No platforms found to build for!")
|
||||||
|
|
||||||
|
for platform in platforms:
|
||||||
|
if platform.startswith("win"):
|
||||||
|
self.build(Filename(outputDir, platform + "/" + self.basename + ".exe"), platform)
|
||||||
|
else:
|
||||||
|
self.build(Filename(outputDir, platform + "/" + self.basename), platform)
|
||||||
|
|
||||||
|
def build(self, output, platform = None):
|
||||||
|
""" Builds a standalone executable and stores it into the path
|
||||||
|
indicated by the 'output' argument. You can specify to build for
|
||||||
|
other platforms by altering the 'platform' argument. """
|
||||||
|
|
||||||
|
if platform == None:
|
||||||
|
platform = PandaSystem.getPlatform()
|
||||||
|
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
|
||||||
|
|
||||||
|
# Figure out where p3dembed might be now.
|
||||||
|
if package.platform.startswith("win"):
|
||||||
|
p3dembed = Filename(self.host.hostDir, "p3dembed/%s/p3dembed.exe" % package.platform)
|
||||||
|
else:
|
||||||
|
p3dembed = Filename(self.host.hostDir, "p3dembed/%s/p3dembed" % package.platform)
|
||||||
|
|
||||||
|
# We allow p3dembed to be pzipped.
|
||||||
|
if Filename(p3dembed + ".pz").exists():
|
||||||
|
p3dembed = Filename(p3dembed + ".pz")
|
||||||
|
if not p3dembed.exists():
|
||||||
|
Standalone.notify.error(" -> %s failed for platform %s" % (package.packageName, package.platform))
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.embed(output, p3dembed)
|
||||||
|
return
|
||||||
|
|
||||||
|
Standalone.notify.error("Failed to build standalone for platform %s" % platform)
|
||||||
|
|
||||||
|
def embed(self, output, p3dembed):
|
||||||
|
""" Embeds the p3d file into the provided p3dembed executable.
|
||||||
|
This function is not really useful - use build() or buildAll() instead. """
|
||||||
|
|
||||||
|
# Load the p3dembed data into memory
|
||||||
|
size = p3dembed.getFileSize()
|
||||||
|
p3dembed_data = VirtualFileSystem.getGlobalPtr().readFile(p3dembed, True)
|
||||||
|
assert len(p3dembed_data) == size
|
||||||
|
|
||||||
|
# Find the magic size string and replace it with the real size,
|
||||||
|
# regardless of the endianness of the p3dembed executable.
|
||||||
|
hex_size = hex(size)[2:].rjust(8, "0")
|
||||||
|
enc_size = "".join([chr(int(hex_size[i] + hex_size[i + 1], 16)) for i in range(0, len(hex_size), 2)])
|
||||||
|
p3dembed_data = p3dembed_data.replace(P3DEMBED_MAGIC, enc_size)
|
||||||
|
p3dembed_data = p3dembed_data.replace(P3DEMBED_MAGIC[::-1], enc_size[::-1])
|
||||||
|
|
||||||
|
# Write the output file
|
||||||
|
Standalone.notify.info("Creating %s..." % output)
|
||||||
|
output.makeDir()
|
||||||
|
ohandle = open(output.toOsSpecific(), "wb")
|
||||||
|
ohandle.write(p3dembed_data)
|
||||||
|
for token in self.tokens.items():
|
||||||
|
ohandle.write("\0%s=%s" % token)
|
||||||
|
ohandle.write("\0\0")
|
||||||
|
|
||||||
|
# Buffer the p3d file to the output file. 1 MB buffer size.
|
||||||
|
phandle = open(self.p3dfile.toOsSpecific(), "rb")
|
||||||
|
buf = phandle.read(1024 * 1024)
|
||||||
|
while len(buf) != 0:
|
||||||
|
ohandle.write(buf)
|
||||||
|
buf = phandle.read(1024 * 1024)
|
||||||
|
ohandle.close()
|
||||||
|
phandle.close()
|
||||||
|
|
||||||
|
os.chmod(output.toOsSpecific(), 0755)
|
||||||
|
|
||||||
|
class Installer:
|
||||||
|
""" This class creates a (graphical) installer from a given .p3d file. """
|
||||||
|
notify = directNotify.newCategory("Installer")
|
||||||
|
|
||||||
|
def __init__(self, shortname, fullname, p3dfile, version):
|
||||||
|
self.shortname = shortname
|
||||||
|
self.fullname = fullname
|
||||||
|
self.version = str(version)
|
||||||
|
self.licensename = ""
|
||||||
|
self.authorid = "org.panda3d"
|
||||||
|
self.authorname = ""
|
||||||
|
self.licensefile = Filename()
|
||||||
|
self.builder = StandaloneBuilder(p3dfile)
|
||||||
|
|
||||||
|
def buildAll(self):
|
||||||
|
""" Creates a (graphical) installer for every known platform.
|
||||||
|
Call this after you have set the desired parameters. """
|
||||||
|
|
||||||
|
# Download the 'p3dembed' package
|
||||||
|
tempdir = Filename.temporary("", self.shortname + "_p3d_", "") + "/"
|
||||||
|
tempdir.makeDir()
|
||||||
|
host = HostInfo("http://runtime.panda3d.org", hostDir = tempdir, asMirror = True)
|
||||||
|
tempdir = tempdir.toOsSpecific()
|
||||||
|
http = HTTPClient.getGlobalPtr()
|
||||||
|
if not host.downloadContentsFile(http):
|
||||||
|
Installer.notify.error("couldn't read host")
|
||||||
|
return False
|
||||||
|
|
||||||
|
for package in host.getPackages(name = "p3dembed"):
|
||||||
|
print package.packageName, package.packageVersion, package.platform
|
||||||
|
if not package.downloadDescFile(http):
|
||||||
|
Installer.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
|
||||||
|
continue
|
||||||
|
if not package.downloadPackage(http):
|
||||||
|
Installer.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if package.platform.startswith("linux_"):
|
||||||
|
plugin_standalone = os.path.join(tempdir, "plugin_standalone", package.platform, "panda3d")
|
||||||
|
assert os.path.isfile(plugin_standalone)
|
||||||
|
self.__buildDEB(plugin_standalone, arch = package.platform.replace("linux_", ""))
|
||||||
|
elif package.platform.startswith("win"):
|
||||||
|
plugin_standalone = os.path.join(tempdir, "plugin_standalone", package.platform, "panda3d.exe")
|
||||||
|
assert os.path.isfile(plugin_standalone)
|
||||||
|
self.__buildNSIS(plugin_standalone)
|
||||||
|
elif package.platform == "darwin":
|
||||||
|
plugin_standalone = os.path.join(tempdir, "plugin_standalone", package.platform, "panda3d_mac")
|
||||||
|
assert os.path.isfile(plugin_standalone)
|
||||||
|
self.__buildAPP(plugin_standalone)
|
||||||
|
else:
|
||||||
|
Installer.notify.info("Ignoring unknown platform " + package.platform)
|
||||||
|
|
||||||
|
#shutil.rmtree(tempdir)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __buildDEB(self, plugin_standalone, arch = "all"):
|
||||||
|
debfn = "%s_%s_all.deb" % (self.shortname.lower(), self.version)
|
||||||
|
Installer.notify.info("Creating %s..." % debfn)
|
||||||
|
|
||||||
|
# Create a temporary directory and write the control file + launcher to it
|
||||||
|
tempdir = Filename.temporary("", self.shortname.lower() + "_deb_", "") + "/"
|
||||||
|
tempdir.makeDir()
|
||||||
|
tempdir = tempdir.toOsSpecific()
|
||||||
|
controlfile = open(os.path.join(tempdir, "control"), "w")
|
||||||
|
controlfile.write("Package: %s\n" % self.shortname.lower())
|
||||||
|
controlfile.write("Version: %s\n" % self.version)
|
||||||
|
controlfile.write("Section: games\n")
|
||||||
|
controlfile.write("Priority: optional\n")
|
||||||
|
controlfile.write("Architecture: %s\n" % arch)
|
||||||
|
controlfile.write("Description: %s\n" % self.fullname)
|
||||||
|
controlfile.close()
|
||||||
|
os.makedirs(os.path.join(tempdir, "usr", "bin"))
|
||||||
|
os.makedirs(os.path.join(tempdir, "usr", "share", "games", self.shortname.lower()))
|
||||||
|
os.makedirs(os.path.join(tempdir, "usr", "libexec", self.shortname.lower()))
|
||||||
|
if not self.licensefile.empty():
|
||||||
|
os.makedirs(os.path.join(tempdir, "usr", "share", "doc", self.shortname.lower()))
|
||||||
|
launcherfile = open(os.path.join(tempdir, "usr", "bin", self.shortname.lower()), "w")
|
||||||
|
launcherfile.write("#!/bin/sh\n")
|
||||||
|
launcherfile.write("/usr/libexec/%s/panda3d /usr/share/games/%s/%s.p3d\n" % ((self.shortname.lower(),) * 3))
|
||||||
|
launcherfile.close()
|
||||||
|
os.chmod(os.path.join(tempdir, "usr", "bin", self.shortname.lower()), 0755)
|
||||||
|
shutil.copyfile(self.p3dfile.toOsSpecific(), os.path.join(tempdir, "usr", "share", "games", self.shortname.lower(), self.shortname.lower() + ".p3d"))
|
||||||
|
shutil.copyfile(plugin_standalone, os.path.join(tempdir, "usr", "libexec", self.shortname.lower(), "panda3d"))
|
||||||
|
if not self.licensefile.empty():
|
||||||
|
shutil.copyfile(self.licensefile.toOsSpecific(), os.path.join(tempdir, "usr", "share", "doc", self.shortname.lower(), "copyright"))
|
||||||
|
|
||||||
|
# Create a control.tar.gz file in memory
|
||||||
|
controltargz = CachedFile()
|
||||||
|
controltarfile = tarfile.TarFile.gzopen("control.tar.gz", "w", controltargz, 9)
|
||||||
|
controltarfile.add(os.path.join(tempdir, "control"), "control")
|
||||||
|
controltarfile.close()
|
||||||
|
os.remove(os.path.join(tempdir, "control"))
|
||||||
|
|
||||||
|
# Create the data.tar.gz file in the temporary directory
|
||||||
|
datatargz = CachedFile()
|
||||||
|
datatarfile = tarfile.TarFile.gzopen("data.tar.gz", "w", datatargz, 9)
|
||||||
|
datatarfile.add(tempdir + "/usr", "/usr")
|
||||||
|
datatarfile.close()
|
||||||
|
|
||||||
|
# Open the deb file and write to it. It's actually
|
||||||
|
# just an AR file, which is very easy to make.
|
||||||
|
modtime = int(time.time())
|
||||||
|
if os.path.isfile(debfn):
|
||||||
|
os.remove(debfn)
|
||||||
|
debfile = open(debfn, "wb")
|
||||||
|
debfile.write("!<arch>\x0A")
|
||||||
|
debfile.write("debian-binary %-12lu0 0 100644 %-10ld\x60\x0A" % (modtime, 4))
|
||||||
|
debfile.write("2.0\x0A")
|
||||||
|
debfile.write("control.tar.gz %-12lu0 0 100644 %-10ld\x60\x0A" % (modtime, len(controltargz.str)))
|
||||||
|
debfile.write(controltargz.str)
|
||||||
|
if (len(controltargz.str) & 1): debfile.write("\x0A")
|
||||||
|
debfile.write("data.tar.gz %-12lu0 0 100644 %-10ld\x60\x0A" % (modtime, len(datatargz.str)))
|
||||||
|
debfile.write(datatargz.str)
|
||||||
|
if (len(datatargz.str) & 1): debfile.write("\x0A")
|
||||||
|
debfile.close()
|
||||||
|
shutil.rmtree(tempdir)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
# 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"))
|
||||||
|
|
||||||
|
# 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 application plist file.
|
||||||
|
# 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.
|
||||||
|
plist = open(os.path.join(tempdir, appname.lstrip("/"), "Contents", "Info.plist"), "w")
|
||||||
|
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, '<plist version="1.0">'
|
||||||
|
print >>plist, '<dict>'
|
||||||
|
print >>plist, '\t<key>CFBundleDevelopmentRegion</key>'
|
||||||
|
print >>plist, '\t<string>English</string>'
|
||||||
|
print >>plist, '\t<key>CFBundleDisplayName</key>'
|
||||||
|
print >>plist, '\t<string>%s</string>' % self.fullname
|
||||||
|
print >>plist, '\t<key>CFBundleExecutable</key>'
|
||||||
|
print >>plist, '\t<string>launch</string>'
|
||||||
|
print >>plist, '\t<key>CFBundleIdentifier</key>'
|
||||||
|
print >>plist, '\t<string>%s.%s</string>' % (self.authorid, self.shortname)
|
||||||
|
print >>plist, '\t<key>CFBundleInfoDictionaryVersion</key>'
|
||||||
|
print >>plist, '\t<string>6.0</string>'
|
||||||
|
print >>plist, '\t<key>CFBundleName</key>'
|
||||||
|
print >>plist, '\t<string>%s</string>' % self.shortname
|
||||||
|
print >>plist, '\t<key>CFBundlePackageType</key>'
|
||||||
|
print >>plist, '\t<string>APPL</string>'
|
||||||
|
print >>plist, '\t<key>CFBundleShortVersionString</key>'
|
||||||
|
print >>plist, '\t<string>%s</string>' % self.version
|
||||||
|
print >>plist, '\t<key>CFBundleVersion</key>'
|
||||||
|
print >>plist, '\t<string>%s</string>' % self.version
|
||||||
|
print >>plist, '\t<key>LSHasLocalizedDisplayName</key>'
|
||||||
|
print >>plist, '\t<false/>'
|
||||||
|
print >>plist, '\t<key>NSAppleScriptEnabled</key>'
|
||||||
|
print >>plist, '\t<false/>'
|
||||||
|
print >>plist, '\t<key>NSPrincipalClass</key>'
|
||||||
|
print >>plist, '\t<string>NSApplication</string>'
|
||||||
|
print >>plist, '</dict>'
|
||||||
|
print >>plist, '</plist>'
|
||||||
|
plist.close()
|
||||||
|
|
||||||
|
def __buildNSIS(self):
|
||||||
|
# Check if we have makensis first
|
||||||
|
makensis = None
|
||||||
|
if (sys.platform.startswith("win")):
|
||||||
|
for p in os.defpath.split(";") + os.environ["PATH"].split(";"):
|
||||||
|
if os.path.isfile(os.path.join(p, "makensis.exe")):
|
||||||
|
makensis = os.path.join(p, "makensis.exe")
|
||||||
|
if not makensis:
|
||||||
|
import pandac
|
||||||
|
makensis = os.path.dirname(os.path.dirname(pandac.__file__))
|
||||||
|
makensis = os.path.join(makensis, "nsis", "makensis.exe")
|
||||||
|
if not os.path.isfile(makensis): makensis = None
|
||||||
|
else:
|
||||||
|
for p in os.defpath.split(":") + os.environ["PATH"].split(":"):
|
||||||
|
if os.path.isfile(os.path.join(p, "makensis")):
|
||||||
|
makensis = os.path.join(p, "makensis")
|
||||||
|
|
||||||
|
if makensis == None:
|
||||||
|
Installer.notify.warning("Makensis utility not found, no Windows installer will be built!")
|
||||||
|
return
|
||||||
|
Installer.notify.info("Creating %s.exe..." % self.shortname)
|
||||||
|
|
||||||
|
tempfile = self.shortname + ".nsi"
|
||||||
|
nsi = open(tempfile, "w")
|
||||||
|
|
||||||
|
# Some global info
|
||||||
|
nsi.write('Name "%s"\n' % self.fullname)
|
||||||
|
nsi.write('OutFile "%s.exe"\n' % self.shortname)
|
||||||
|
nsi.write('InstallDir "$PROGRAMFILES\%s"\n' % self.fullname)
|
||||||
|
nsi.write('SetCompress auto\n')
|
||||||
|
nsi.write('SetCompressor lzma\n')
|
||||||
|
nsi.write('ShowInstDetails nevershow\n')
|
||||||
|
nsi.write('ShowUninstDetails nevershow\n')
|
||||||
|
nsi.write('InstType "Typical"\n')
|
||||||
|
|
||||||
|
# Tell Vista that we require admin rights
|
||||||
|
nsi.write('RequestExecutionLevel admin\n')
|
||||||
|
nsi.write('\n')
|
||||||
|
nsi.write('Function launch\n')
|
||||||
|
nsi.write(' ExecShell "open" "$INSTDIR\%s.bat"\n' % self.shortname)
|
||||||
|
nsi.write('FunctionEnd\n')
|
||||||
|
nsi.write('\n')
|
||||||
|
nsi.write('!include "MUI2.nsh"\n')
|
||||||
|
nsi.write('!define MUI_ABORTWARNING\n')
|
||||||
|
nsi.write('!define MUI_FINISHPAGE_RUN\n')
|
||||||
|
nsi.write('!define MUI_FINISHPAGE_RUN_FUNCTION launch\n')
|
||||||
|
nsi.write('!define MUI_FINISHPAGE_RUN_TEXT "Play %s"\n' % self.fullname)
|
||||||
|
nsi.write('\n')
|
||||||
|
nsi.write('Var StartMenuFolder\n')
|
||||||
|
nsi.write('!insertmacro MUI_PAGE_WELCOME\n')
|
||||||
|
if not self.licensefile.empty():
|
||||||
|
nsi.write('!insertmacro MUI_PAGE_LICENSE "%s"\n' % self.licensefile.toOsSpecific())
|
||||||
|
nsi.write('!insertmacro MUI_PAGE_DIRECTORY\n')
|
||||||
|
nsi.write('!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder\n')
|
||||||
|
nsi.write('!insertmacro MUI_PAGE_INSTFILES\n')
|
||||||
|
nsi.write('!insertmacro MUI_PAGE_FINISH\n')
|
||||||
|
nsi.write('!insertmacro MUI_UNPAGE_WELCOME\n')
|
||||||
|
nsi.write('!insertmacro MUI_UNPAGE_CONFIRM\n')
|
||||||
|
nsi.write('!insertmacro MUI_UNPAGE_INSTFILES\n')
|
||||||
|
nsi.write('!insertmacro MUI_UNPAGE_FINISH\n')
|
||||||
|
nsi.write('!insertmacro MUI_LANGUAGE "English"\n')
|
||||||
|
|
||||||
|
# This section defines the installer.
|
||||||
|
nsi.write('Section "Install"\n')
|
||||||
|
nsi.write(' SetOutPath "$INSTDIR"\n')
|
||||||
|
nsi.write(' WriteUninstaller "$INSTDIR\Uninstall.exe"\n')
|
||||||
|
nsi.write(' ; Start menu items\n')
|
||||||
|
nsi.write(' !insertmacro MUI_STARTMENU_WRITE_BEGIN Application\n')
|
||||||
|
nsi.write(' CreateDirectory "$SMPROGRAMS\$StartMenuFolder"\n')
|
||||||
|
nsi.write(' CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe"\n')
|
||||||
|
nsi.write(' !insertmacro MUI_STARTMENU_WRITE_END\n')
|
||||||
|
nsi.write('SectionEnd\n')
|
||||||
|
|
||||||
|
# This section defines the uninstaller.
|
||||||
|
nsi.write('Section "Uninstall"\n')
|
||||||
|
nsi.write(' Delete "$INSTDIR\Uninstall.exe"\n')
|
||||||
|
nsi.write(' RMDir "$INSTDIR"\n')
|
||||||
|
nsi.write(' ; Start menu items\n')
|
||||||
|
nsi.write(' !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder\n')
|
||||||
|
nsi.write(' Delete "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk"\n')
|
||||||
|
nsi.write(' RMDir "$SMPROGRAMS\$StartMenuFolder"\n')
|
||||||
|
nsi.write('SectionEnd')
|
||||||
|
nsi.close()
|
||||||
|
|
||||||
|
options = ["V2"]
|
||||||
|
cmd = makensis
|
||||||
|
for o in options:
|
||||||
|
if sys.platform.startswith("win"):
|
||||||
|
cmd += " /" + o
|
||||||
|
else:
|
||||||
|
cmd += " -" + o
|
||||||
|
cmd += " " + tempfile
|
||||||
|
os.system(cmd)
|
||||||
|
|
||||||
|
os.remove(tempfile)
|
@ -1,189 +0,0 @@
|
|||||||
""" This module is used to build a graphical installer
|
|
||||||
from a p3d file. It will try to build installers for as
|
|
||||||
many platforms as possible. """
|
|
||||||
|
|
||||||
__all__ = ["InstallerMaker"]
|
|
||||||
|
|
||||||
import os, sys, subprocess, tarfile, shutil, time
|
|
||||||
from direct.directnotify.DirectNotifyGlobal import *
|
|
||||||
from pandac.PandaModules import Filename
|
|
||||||
|
|
||||||
class CachedFile:
|
|
||||||
def __init__(self): self.str = ""
|
|
||||||
def write(self, data): self.str += data
|
|
||||||
|
|
||||||
class InstallerMaker:
|
|
||||||
notify = directNotify.newCategory("InstallerMaker")
|
|
||||||
|
|
||||||
def __init__(self, shortname, fullname, p3dfile, version):
|
|
||||||
self.shortname = shortname
|
|
||||||
self.fullname = fullname
|
|
||||||
self.version = str(version)
|
|
||||||
self.licensename = ""
|
|
||||||
# All paths given must be a Filename instance!
|
|
||||||
assert isinstance(p3dfile, Filename)
|
|
||||||
self.p3dfile = p3dfile
|
|
||||||
self.licensefile = Filename()
|
|
||||||
|
|
||||||
def build(self):
|
|
||||||
""" Creates the installer. Call this after you have set all the parameters. """
|
|
||||||
self.__buildDEB()
|
|
||||||
self.__buildNSIS()
|
|
||||||
|
|
||||||
def __buildDEB(self):
|
|
||||||
debfn = "%s_%s_all.deb" % (self.shortname, self.version)
|
|
||||||
InstallerMaker.notify.info("Creating %s..." % debfn)
|
|
||||||
|
|
||||||
# Create a temporary directory and write the control file + launcher to it
|
|
||||||
tempdir = Filename.temporary("", self.shortname + "_deb_", "") + "/"
|
|
||||||
tempdir.makeDir()
|
|
||||||
tempdir = tempdir.toOsSpecific()
|
|
||||||
controlfile = open(os.path.join(tempdir, "control"), "w")
|
|
||||||
controlfile.write("Package: %s\n" % self.shortname)
|
|
||||||
controlfile.write("Version: %s\n" % self.version)
|
|
||||||
controlfile.write("Section: games\n")
|
|
||||||
controlfile.write("Priority: optional\n")
|
|
||||||
controlfile.write("Architecture: all\n")
|
|
||||||
controlfile.write("Depends: panda3d-runtime\n")
|
|
||||||
controlfile.write("Description: %s\n" % self.fullname)
|
|
||||||
controlfile.close()
|
|
||||||
os.makedirs(os.path.join(tempdir, "usr", "bin"))
|
|
||||||
os.makedirs(os.path.join(tempdir, "usr", "share", "games", self.shortname))
|
|
||||||
if not self.licensefile.empty():
|
|
||||||
os.makedirs(os.path.join(tempdir, "usr", "share", "doc", self.shortname))
|
|
||||||
launcherfile = open(os.path.join(tempdir, "usr", "bin", self.shortname), "w")
|
|
||||||
launcherfile.write("#!/bin/sh\n")
|
|
||||||
launcherfile.write("/usr/bin/env panda3d /usr/share/games/%s/data.p3d\n" % self.shortname)
|
|
||||||
launcherfile.close()
|
|
||||||
os.chmod(os.path.join(tempdir, "usr", "bin", self.shortname), 0755)
|
|
||||||
shutil.copyfile(self.p3dfile.toOsSpecific(), os.path.join(tempdir, "usr", "share", "games", self.shortname, "data.p3d"))
|
|
||||||
if not self.licensefile.empty():
|
|
||||||
shutil.copyfile(self.licensefile.toOsSpecific(), os.path.join(tempdir, "usr", "share", "doc", self.shortname, "copyright"))
|
|
||||||
|
|
||||||
# Create a control.tar.gz file in memory
|
|
||||||
controltargz = CachedFile()
|
|
||||||
controltarfile = tarfile.TarFile.gzopen("control.tar.gz", "w", controltargz, 9)
|
|
||||||
controltarfile.add(os.path.join(tempdir, "control"), "control")
|
|
||||||
controltarfile.close()
|
|
||||||
os.remove(os.path.join(tempdir, "control"))
|
|
||||||
|
|
||||||
# Create the data.tar.gz file in the temporary directory
|
|
||||||
datatargz = CachedFile()
|
|
||||||
datatarfile = tarfile.TarFile.gzopen("data.tar.gz", "w", datatargz, 9)
|
|
||||||
datatarfile.add(tempdir + "/usr", "/usr")
|
|
||||||
datatarfile.close()
|
|
||||||
|
|
||||||
# Open the deb file and write to it. It's actually
|
|
||||||
# just an AR file, which is very easy to make.
|
|
||||||
modtime = int(time.time())
|
|
||||||
if os.path.isfile(debfn):
|
|
||||||
os.remove(debfn)
|
|
||||||
debfile = open(debfn, "wb")
|
|
||||||
debfile.write("!<arch>\x0A")
|
|
||||||
debfile.write("debian-binary %-12lu0 0 100644 %-10ld\x60\x0A" % (modtime, 4))
|
|
||||||
debfile.write("2.0\x0A")
|
|
||||||
debfile.write("control.tar.gz %-12lu0 0 100644 %-10ld\x60\x0A" % (modtime, len(controltargz.str)))
|
|
||||||
debfile.write(controltargz.str)
|
|
||||||
if (len(controltargz.str) & 1): debfile.write("\x0A")
|
|
||||||
debfile.write("data.tar.gz %-12lu0 0 100644 %-10ld\x60\x0A" % (modtime, len(datatargz.str)))
|
|
||||||
debfile.write(datatargz.str)
|
|
||||||
if (len(datatargz.str) & 1): debfile.write("\x0A")
|
|
||||||
debfile.close()
|
|
||||||
shutil.rmtree(tempdir)
|
|
||||||
|
|
||||||
def __buildNSIS(self):
|
|
||||||
# Check if we have makensis first
|
|
||||||
makensis = None
|
|
||||||
if (sys.platform.startswith("win")):
|
|
||||||
for p in os.defpath.split(";") + os.environ["PATH"].split(";"):
|
|
||||||
if os.path.isfile(os.path.join(p, "makensis.exe")):
|
|
||||||
makensis = os.path.join(p, "makensis.exe")
|
|
||||||
if not makensis:
|
|
||||||
import pandac
|
|
||||||
makensis = os.path.dirname(os.path.dirname(pandac.__file__))
|
|
||||||
makensis = os.path.join(makensis, "nsis", "makensis.exe")
|
|
||||||
if not os.path.isfile(makensis): makensis = None
|
|
||||||
else:
|
|
||||||
for p in os.defpath.split(":") + os.environ["PATH"].split(":"):
|
|
||||||
if os.path.isfile(os.path.join(p, "makensis")):
|
|
||||||
makensis = os.path.join(p, "makensis")
|
|
||||||
|
|
||||||
if makensis == None:
|
|
||||||
InstallerMaker.notify.warning("Makensis utility not found, no Windows installer will be built!")
|
|
||||||
return
|
|
||||||
InstallerMaker.notify.info("Creating %s.exe..." % self.shortname)
|
|
||||||
|
|
||||||
tempfile = self.shortname + ".nsi"
|
|
||||||
nsi = open(tempfile, "w")
|
|
||||||
|
|
||||||
# Some global info
|
|
||||||
nsi.write('Name "%s"\n' % self.fullname)
|
|
||||||
nsi.write('OutFile "%s.exe"\n' % self.shortname)
|
|
||||||
nsi.write('InstallDir "$PROGRAMFILES\%s"\n' % self.fullname)
|
|
||||||
nsi.write('SetCompress auto\n')
|
|
||||||
nsi.write('SetCompressor lzma\n')
|
|
||||||
nsi.write('ShowInstDetails nevershow\n')
|
|
||||||
nsi.write('ShowUninstDetails nevershow\n')
|
|
||||||
nsi.write('InstType "Typical"\n')
|
|
||||||
|
|
||||||
# Tell Vista that we require admin rights
|
|
||||||
nsi.write('RequestExecutionLevel admin\n')
|
|
||||||
nsi.write('\n')
|
|
||||||
nsi.write('Function launch\n')
|
|
||||||
nsi.write(' ExecShell "open" "$INSTDIR\%s.bat"\n' % self.shortname)
|
|
||||||
nsi.write('FunctionEnd\n')
|
|
||||||
nsi.write('\n')
|
|
||||||
nsi.write('!include "MUI2.nsh"\n')
|
|
||||||
nsi.write('!define MUI_ABORTWARNING\n')
|
|
||||||
nsi.write('!define MUI_FINISHPAGE_RUN\n')
|
|
||||||
nsi.write('!define MUI_FINISHPAGE_RUN_FUNCTION launch\n')
|
|
||||||
nsi.write('!define MUI_FINISHPAGE_RUN_TEXT "Play %s"\n' % self.fullname)
|
|
||||||
nsi.write('\n')
|
|
||||||
nsi.write('Var StartMenuFolder\n')
|
|
||||||
nsi.write('!insertmacro MUI_PAGE_WELCOME\n')
|
|
||||||
if not self.licensefile.empty():
|
|
||||||
nsi.write('!insertmacro MUI_PAGE_LICENSE "%s"\n' % self.licensefile.toOsSpecific())
|
|
||||||
nsi.write('!insertmacro MUI_PAGE_DIRECTORY\n')
|
|
||||||
nsi.write('!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder\n')
|
|
||||||
nsi.write('!insertmacro MUI_PAGE_INSTFILES\n')
|
|
||||||
nsi.write('!insertmacro MUI_PAGE_FINISH\n')
|
|
||||||
nsi.write('!insertmacro MUI_UNPAGE_WELCOME\n')
|
|
||||||
nsi.write('!insertmacro MUI_UNPAGE_CONFIRM\n')
|
|
||||||
nsi.write('!insertmacro MUI_UNPAGE_INSTFILES\n')
|
|
||||||
nsi.write('!insertmacro MUI_UNPAGE_FINISH\n')
|
|
||||||
nsi.write('!insertmacro MUI_LANGUAGE "English"\n')
|
|
||||||
|
|
||||||
# This section defines the installer.
|
|
||||||
nsi.write('Section "Install"\n')
|
|
||||||
nsi.write(' SetOutPath "$INSTDIR"\n')
|
|
||||||
nsi.write(' WriteUninstaller "$INSTDIR\Uninstall.exe"\n')
|
|
||||||
nsi.write(' ; Start menu items\n')
|
|
||||||
nsi.write(' !insertmacro MUI_STARTMENU_WRITE_BEGIN Application\n')
|
|
||||||
nsi.write(' CreateDirectory "$SMPROGRAMS\$StartMenuFolder"\n')
|
|
||||||
nsi.write(' CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe"\n')
|
|
||||||
nsi.write(' !insertmacro MUI_STARTMENU_WRITE_END\n')
|
|
||||||
nsi.write('SectionEnd\n')
|
|
||||||
|
|
||||||
# This section defines the uninstaller.
|
|
||||||
nsi.write('Section "Uninstall"\n')
|
|
||||||
nsi.write(' Delete "$INSTDIR\Uninstall.exe"\n')
|
|
||||||
nsi.write(' RMDir "$INSTDIR"\n')
|
|
||||||
nsi.write(' ; Start menu items\n')
|
|
||||||
nsi.write(' !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder\n')
|
|
||||||
nsi.write(' Delete "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk"\n')
|
|
||||||
nsi.write(' RMDir "$SMPROGRAMS\$StartMenuFolder"\n')
|
|
||||||
nsi.write('SectionEnd')
|
|
||||||
nsi.close()
|
|
||||||
|
|
||||||
options = ["V2"]
|
|
||||||
cmd = makensis
|
|
||||||
for o in options:
|
|
||||||
if sys.platform.startswith("win"):
|
|
||||||
cmd += " /" + o
|
|
||||||
else:
|
|
||||||
cmd += " -" + o
|
|
||||||
cmd += " " + tempfile
|
|
||||||
os.system(cmd)
|
|
||||||
|
|
||||||
os.remove(tempfile)
|
|
||||||
|
|
@ -320,3 +320,15 @@ class pmerge(p3d):
|
|||||||
require('panda3d')
|
require('panda3d')
|
||||||
|
|
||||||
mainModule('direct.p3d.pmerge')
|
mainModule('direct.p3d.pmerge')
|
||||||
|
|
||||||
|
|
||||||
|
class pdeploy(p3d):
|
||||||
|
# This utility can distribute a game in the form of
|
||||||
|
# a standalone executable or a graphical installer.
|
||||||
|
|
||||||
|
config(display_name = "Panda3D Deployment Tool",
|
||||||
|
hidden = True, platform_specific = False,
|
||||||
|
keep_user_env = True)
|
||||||
|
require('panda3d')
|
||||||
|
|
||||||
|
mainModule('direct.p3d.pdeploy')
|
||||||
|
@ -1,35 +1,35 @@
|
|||||||
#! /usr/bin/env python
|
#! /usr/bin/env python
|
||||||
|
|
||||||
"""
|
usageText = """
|
||||||
|
|
||||||
This command will help you to distribute your Panda game, consisting
|
This command will help you to distribute your Panda application,
|
||||||
of a .p3d file, into an installable package or an HTML webpage.
|
consisting of a .p3d package, into a standalone executable, graphical
|
||||||
|
installer or an HTML webpage. It will attempt to create packages
|
||||||
|
for every platform, if possible.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
%s [opts] app.p3d installer|web
|
%(prog)s [opts] app.p3d standalone|installer|html
|
||||||
|
|
||||||
Modes:
|
Modes:
|
||||||
|
|
||||||
|
standalone
|
||||||
|
A standalone executable will be created that embeds the given
|
||||||
|
p3d file. The resulting executable will require an
|
||||||
|
internet connection in order to run properly.
|
||||||
|
|
||||||
installer
|
installer
|
||||||
In this mode, installable packages will be created for as many
|
In this mode, installable packages will be created for as many
|
||||||
platforms as possible. To create Windows installers on
|
platforms as possible. To create Windows installers on
|
||||||
non-Windows platforms, you need to have the "makensis" utility
|
non-Windows platforms, you need to have the "makensis" utility
|
||||||
on your system PATH environment variable.
|
on your system PATH environment variable.
|
||||||
|
|
||||||
web
|
html
|
||||||
An HTML webpage will be generated that can be used to view
|
An HTML webpage will be generated that can be used to view
|
||||||
the provided p3d file in a browser.
|
the provided p3d file in a browser.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
-v version_number
|
|
||||||
This should define the version number of your application
|
|
||||||
or game. In some deploy modes, this argument is required.
|
|
||||||
This should only contain alphanumeric characters, dots and
|
|
||||||
dashes, as the result of the deployment may be in valid
|
|
||||||
on some platforms otherwise.
|
|
||||||
|
|
||||||
-n your_app
|
-n your_app
|
||||||
Short, lowercase name of the application or game. Can only
|
Short, lowercase name of the application or game. Can only
|
||||||
contain alphanumeric characters, underscore or dash. This
|
contain alphanumeric characters, underscore or dash. This
|
||||||
@ -37,89 +37,168 @@ Options:
|
|||||||
If omitted, the basename of the p3d file is used.
|
If omitted, the basename of the p3d file is used.
|
||||||
|
|
||||||
-N "Your Application"
|
-N "Your Application"
|
||||||
Full name of the application or game. This one will be used
|
Full name of the application or game. This value will
|
||||||
to display to the end-user.
|
be used to display to the end-user.
|
||||||
If omitted, the short name is used.
|
If omitted, the short name is used.
|
||||||
|
|
||||||
|
-v version_number
|
||||||
|
This should define the version number of your application
|
||||||
|
or game. In some deploy modes, this argument is required.
|
||||||
|
This should only contain alphanumeric characters, dots and
|
||||||
|
dashes, as otherwise the result of the deployment may be
|
||||||
|
invalid on some platforms.
|
||||||
|
|
||||||
|
-o output_dir
|
||||||
|
Indicates the directory where the output will be stored.
|
||||||
|
Within this directory, subdirectories will be created
|
||||||
|
for every platform, unless -t is provided.
|
||||||
|
If omitted, the current working directory is assumed.
|
||||||
|
|
||||||
|
-t token=value
|
||||||
|
Defines a web token or parameter to pass to the application.
|
||||||
|
Use this to configure how the application will be run.
|
||||||
|
You can pass as many -t options as you need. Examples of
|
||||||
|
tokens are width, height, log_basename, auto_start, hidden.
|
||||||
|
|
||||||
|
-P platform
|
||||||
|
If this option is provided, it should specify a comma-
|
||||||
|
separated list of platforms that the p3d package will be
|
||||||
|
deployed for. If omitted, it will be built for all platforms.
|
||||||
|
This option may be specified multiple times.
|
||||||
|
Examples of valid platforms are win32, linux_amd64 and osx_ppc.
|
||||||
|
|
||||||
|
-c
|
||||||
|
If this option is provided, the -p option is ignored and
|
||||||
|
the p3d package is only deployed for the current platform.
|
||||||
|
Furthermore, no per-platform subdirectories will be created
|
||||||
|
inside the output dirctory.
|
||||||
|
|
||||||
-l "License Name"
|
-l "License Name"
|
||||||
Specifies the name of the software license that the game
|
Specifies the name of the software license that the game
|
||||||
or application is licensed under.
|
or application is licensed under.
|
||||||
|
Only relevant when generating a graphical installer.
|
||||||
|
|
||||||
-L licensefile.txt
|
-L licensefile.txt
|
||||||
This should point to a file that contains the full text
|
This should point to a file that contains the full text
|
||||||
describing the software license that the game or application
|
describing the software license that the game or application
|
||||||
is licensed under.
|
is licensed under.
|
||||||
|
Only relevant when generating a graphical installer.
|
||||||
|
|
||||||
|
-h
|
||||||
|
Display this help
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
DEPLOY_MODES = ["installer", "web"]
|
DEPLOY_MODES = ["standalone", "installer", "html"]
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import getopt
|
import getopt
|
||||||
from direct.p3d import InstallerMaker
|
from direct.p3d.DeploymentTools import Standalone, Installer
|
||||||
from pandac.PandaModules import Filename
|
from pandac.PandaModules import Filename, PandaSystem
|
||||||
|
|
||||||
class ArgumentError(StandardError):
|
def usage(code, msg = ''):
|
||||||
pass
|
print >> sys.stderr, usageText % {'prog' : os.path.split(sys.argv[0])[1]}
|
||||||
|
print >> sys.stderr, msg
|
||||||
|
sys.exit(code)
|
||||||
|
|
||||||
def deployApp(args):
|
shortname = ""
|
||||||
opts, args = getopt.getopt(args, 'l:L:n:N:v:h')
|
fullname = ""
|
||||||
|
version = ""
|
||||||
|
outputDir = Filename("./")
|
||||||
|
tokens = {}
|
||||||
|
platforms = []
|
||||||
|
currentPlatform = False
|
||||||
|
licensename = ""
|
||||||
|
licensefile = Filename()
|
||||||
|
|
||||||
version = ""
|
try:
|
||||||
shortname = ""
|
opts, args = getopt.getopt(sys.argv[1:], 'n:N:v:o:t:P:cl:L:h')
|
||||||
fullname = ""
|
except getopt.error, msg:
|
||||||
licensename = ""
|
usage(1, msg)
|
||||||
licensefile = Filename()
|
|
||||||
|
|
||||||
for option, value in opts:
|
for opt, arg in opts:
|
||||||
if option == '-v':
|
if opt == '-n':
|
||||||
version = value.strip()
|
shortname = arg.strip()
|
||||||
if option == '-n':
|
elif opt == '-N':
|
||||||
shortname = value.strip()
|
fullname = arg.strip()
|
||||||
elif option == '-L':
|
elif opt == '-v':
|
||||||
fullname = value.strip()
|
version = arg.strip()
|
||||||
if option == '-l':
|
elif opt == '-o':
|
||||||
licensename = value
|
outputDir = Filename.fromOsSpecific(arg)
|
||||||
elif option == '-L':
|
elif opt == '-t':
|
||||||
licensefile = Filename.fromOsSpecific(value)
|
token = arg.strip().split("=", 1)
|
||||||
elif option == '-h':
|
tokens[token[0]] = token[1]
|
||||||
print __doc__ % (os.path.split(sys.argv[0])[1])
|
elif opt == '-P':
|
||||||
|
platforms.append(arg)
|
||||||
|
elif opt == '-c':
|
||||||
|
currentPlatform = True
|
||||||
|
elif opt == '-l':
|
||||||
|
licensename = arg.strip()
|
||||||
|
elif opt == '-L':
|
||||||
|
licensefile = Filename.fromOsSpecific(arg)
|
||||||
|
|
||||||
|
elif opt == '-h':
|
||||||
|
usage(0)
|
||||||
|
else:
|
||||||
|
print 'illegal option: ' + flag
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if not args or len(args) < 2:
|
if not args or len(args) != 2:
|
||||||
raise ArgumentError, "No target app and/or deploy type specified. Use:\n%s app.p3d %s" % (os.path.split(sys.argv[0])[1], '|'.join(DEPLOY_MODES))
|
usage(1)
|
||||||
|
|
||||||
if len(args) > 2:
|
appFilename = Filename.fromOsSpecific(args[0])
|
||||||
raise ArgumentError, "Too many arguments."
|
if appFilename.getExtension().lower() != 'p3d':
|
||||||
|
print 'Application filename must end in ".p3d".'
|
||||||
|
sys.exit(1)
|
||||||
|
deploy_mode = args[1].lower()
|
||||||
|
|
||||||
appFilename = Filename.fromOsSpecific(args[0])
|
if not appFilename.exists():
|
||||||
if appFilename.getExtension().lower() != 'p3d':
|
print 'Application filename does not exist!'
|
||||||
raise ArgumentError, 'Application filename must end in ".p3d".'
|
sys.exit(1)
|
||||||
|
|
||||||
deploy_mode = args[1].lower()
|
if shortname.lower() != shortname or ' ' in shortname:
|
||||||
if deploy_mode not in DEPLOY_MODES:
|
print '\nProvided short name should be lowercase, and may not contain spaces!\n'
|
||||||
raise ArgumentError, 'Invalid deploy type, must be one of "%s".' % '", "'.join(DEPLOY_MODES)
|
|
||||||
|
|
||||||
if shortname.lower() != shortname or ' ' in shortname:
|
if shortname == '':
|
||||||
raise ArgumentError, 'Provided short name should be lowercase, and may not contain spaces!'
|
|
||||||
|
|
||||||
if shortname == '':
|
|
||||||
shortname = appFilename.getBasenameWoExtension()
|
shortname = appFilename.getBasenameWoExtension()
|
||||||
|
|
||||||
if fullname == '':
|
if fullname == '':
|
||||||
fullname = shortname
|
fullname = shortname
|
||||||
|
|
||||||
if version == '' and deploy_mode == 'installer':
|
if version == '' and deploy_mode == 'installer':
|
||||||
raise ArgumentError, 'A version number is required in "installer" mode!'
|
print '\nA version number is required in "installer" mode.\n'
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
try:
|
if not outputDir:
|
||||||
if deploy_mode == 'installer':
|
print '\nYou must name the output directory with the -o parameter.\n'
|
||||||
im = InstallerMaker.InstallerMaker(shortname, fullname, appFilename, version)
|
sys.exit(1)
|
||||||
im.licensename = licensename
|
|
||||||
im.licensefile = licensefile
|
if deploy_mode == 'standalone':
|
||||||
im.build()
|
s = Standalone(appFilename, tokens)
|
||||||
elif deploy_mode == 'web':
|
s.basename = shortname
|
||||||
|
|
||||||
|
if currentPlatform:
|
||||||
|
platform = PandaSystem.getPlatform()
|
||||||
|
if platform.startswith("win"):
|
||||||
|
s.build(Filename(outputDir, shortname + ".exe"), platform)
|
||||||
|
else:
|
||||||
|
s.build(Filename(outputDir, shortname), platform)
|
||||||
|
elif len(platforms) == 0:
|
||||||
|
s.buildAll(outputDir)
|
||||||
|
else:
|
||||||
|
for platform in platforms:
|
||||||
|
if platform.startswith("win"):
|
||||||
|
s.build(Filename(outputDir, platform + "/" + shortname + ".exe"), platform)
|
||||||
|
else:
|
||||||
|
s.build(Filename(outputDir, platform + "/" + shortname), platform)
|
||||||
|
|
||||||
|
elif deploy_mode == 'installer':
|
||||||
|
i = Installer(shortname, fullname, appFilename, version)
|
||||||
|
i.licensename = licensename
|
||||||
|
i.licensefile = licensefile
|
||||||
|
i.build()
|
||||||
|
elif deploy_mode == 'html':
|
||||||
print "Creating %s.html..." % shortname
|
print "Creating %s.html..." % shortname
|
||||||
html = open(shortname + ".html", "w")
|
html = open(shortname + ".html", "w")
|
||||||
html.write("<html>\n")
|
html.write("<html>\n")
|
||||||
@ -131,18 +210,9 @@ def deployApp(args):
|
|||||||
html.write(" </body>\n")
|
html.write(" </body>\n")
|
||||||
html.write("</html>\n")
|
html.write("</html>\n")
|
||||||
html.close()
|
html.close()
|
||||||
|
else:
|
||||||
|
usage(1, 'Invalid deployment mode!')
|
||||||
|
|
||||||
except: raise
|
# An explicit call to exit() is required to exit the program, when
|
||||||
#except InstallerMaker.InstallerMakerError:
|
# this module is packaged in a p3d file.
|
||||||
# # Just print the error message and exit gracefully.
|
sys.exit(0)
|
||||||
# inst = sys.exc_info()[1]
|
|
||||||
# print inst.args[0]
|
|
||||||
# sys.exit(1)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
try:
|
|
||||||
deployApp(sys.argv[1:])
|
|
||||||
except ArgumentError, e:
|
|
||||||
print e.args[0]
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user