Changes to pdeploy, add ArchLinux support, better Linux stuff etc

This commit is contained in:
rdb 2011-02-27 14:50:53 +00:00
parent 461fd1968c
commit a16fd9f23b
2 changed files with 249 additions and 143 deletions

View File

@ -5,6 +5,7 @@ to build for as many platforms as possible. """
__all__ = ["Standalone", "Installer"]
import os, sys, subprocess, tarfile, shutil, time, zipfile, glob
from cStringIO import StringIO
from direct.directnotify.DirectNotifyGlobal import *
from direct.showbase.AppRunnerGlobal import appRunner
from pandac.PandaModules import PandaSystem, HTTPClient, Filename, VirtualFileSystem, Multifile
@ -13,43 +14,72 @@ from direct.p3d.HostInfo import HostInfo
# This is important for some reason
import encodings
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"
# This filter function is used when creating
# an archive that should be owned by root.
def archiveFilter(info):
basename = os.path.basename(info.name)
if basename in [".DS_Store", "Thumbs.db"]:
return None
info.uid = info.gid = 0
info.uname = info.gname = "root"
return info
# Hack to make all files in a tar file owned by root.
# The tarfile module doesn't have filter functionality until Python 2.7.
# Yay duck typing!
class TarInfoRoot(tarfile.TarInfo):
uid = property(lambda self: 0, lambda self, x: None)
gid = property(lambda self: 0, lambda self, x: None)
uname = property(lambda self: "root", lambda self, x: None)
gname = property(lambda self: "root", lambda self, x: None)
# On OSX, the root group is named "wheel".
class TarInfoRootOSX(TarInfoRoot):
gname = property(lambda self: "wheel", lambda self, x: None)
class Standalone:
""" This class creates a standalone executable from a given .p3d file. """
notify = directNotify.newCategory("Standalone")
def __init__(self, p3dfile, tokens = {}):
self.p3dfile = Filename(p3dfile)
self.basename = self.p3dfile.getBasenameWoExtension()
self.tokens = tokens
hostDir = Filename(Filename.getTempDirectory(), 'pdeploy/')
hostDir.makeDir()
self.host = HostInfo(PandaSystem.getPackageHostUrl(), appRunner = appRunner, hostDir = hostDir, asMirror = False, perPlatform = True)
self.tempDir = Filename.temporary("", self.basename, "") + "/"
self.tempDir.makeDir()
self.host = HostInfo(PandaSystem.getPackageHostUrl(), appRunner = appRunner, hostDir = self.tempDir, asMirror = False, perPlatform = True)
self.http = HTTPClient.getGlobalPtr()
if not self.host.hasContentsFile:
if not self.host.readContentsFile():
if not self.host.downloadContentsFile(self.http):
Standalone.notify.error("couldn't read host")
return False
return
def __del__(self):
try:
appRunner.rmtree(self.tempDir)
except:
try: shutil.rmtree(self.tempDir.toOsSpecific())
except: pass
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.warning("No platforms found to build for!")
outputDir = Filename(outputDir + "/")
outputDir.makeDir()
for platform in platforms:
@ -57,17 +87,17 @@ class Standalone:
self.build(Filename(outputDir, platform + "/" + self.basename + ".exe"), platform)
else:
self.build(Filename(outputDir, platform + "/" + self.basename), platform)
def build(self, output, platform = None):
def build(self, output, platform = None, extraTokens = {}):
""" Builds a standalone executable and stores it into the path
indicated by the 'output' argument. You can specify to build for
a different platform by altering the 'platform' argument. """
if platform == None:
platform = PandaSystem.getPlatform()
vfs = VirtualFileSystem.getGlobalPtr()
for package in self.host.getPackages(name = "p3dembed", platform = platform):
if not package.downloadDescFile(self.http):
Standalone.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
@ -75,7 +105,7 @@ class Standalone:
if not package.downloadPackage(self.http):
Standalone.notify.warning(" -> %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)
@ -85,40 +115,41 @@ class Standalone:
if not vfs.exists(p3dembed):
Standalone.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
continue
return self.embed(output, p3dembed)
return self.embed(output, p3dembed, extraTokens)
Standalone.notify.error("Failed to build standalone for platform %s" % platform)
def embed(self, output, p3dembed):
def embed(self, output, p3dembed, extraTokens = {}):
""" 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)
# Write out the tokens. Set log_basename to the basename by default
tokens = {"log_basename" : self.basename}
tokens.update(self.tokens)
tokens.update(extraTokens)
for token in 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)
@ -127,23 +158,23 @@ class Standalone:
buf = phandle.read(1024 * 1024)
ohandle.close()
phandle.close()
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 are full absolute paths. """
package = self.host.getPackages(name = "p3dembed", platform = platform)[0]
if not package.downloadDescFile(self.http):
Standalone.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
return []
if not package.downloadPackage(self.http):
Standalone.notify.warning(" -> %s failed for platform %s" % (package.packageName, package.platform))
return []
filenames = []
vfs = VirtualFileSystem.getGlobalPtr()
for e in package.extracts:
@ -154,7 +185,7 @@ class Standalone:
filenames.append(filename)
else:
Standalone.notify.error("%s mentioned in xml, but does not exist" % e.filename)
return filenames
class Installer:
@ -173,7 +204,9 @@ class Installer:
self.licensefile = Filename()
self.standalone = Standalone(p3dfile, tokens)
self.http = self.standalone.http
self.tempDir = Filename.temporary("", self.shortname, "") + "/"
self.tempDir.makeDir()
# Load the p3d file to read out the required packages
mf = Multifile()
if not mf.openRead(p3dfile):
@ -200,22 +233,29 @@ class Installer:
self.requirements.append((p3dRequires.Attribute('name'), p3dRequires.Attribute('version')))
p3dRequires = p3dRequires.NextSiblingElement('requires')
def __del__(self):
try:
appRunner.rmtree(self.tempDir)
except:
try: shutil.rmtree(self.tempDir.toOsSpecific())
except: pass
def installPackagesInto(self, hostDir, platform):
""" Installs the packages required by the .p3d file into
the specified directory, for the given platform. """
if not self.includeRequires:
return
packages = []
host = HostInfo(self.hostUrl, appRunner = appRunner, hostDir = hostDir, asMirror = False, perPlatform = False)
if not host.hasContentsFile:
if not host.readContentsFile():
if not host.downloadContentsFile(self.http):
Installer.notify.error("couldn't read host %s" % host.hostUrl)
return
superHost = None
if appRunner.superMirrorUrl:
superHost = HostInfo(appRunner.superMirrorUrl, appRunner = appRunner, hostDir = hostDir, asMirror = False, perPlatform = False)
@ -224,7 +264,7 @@ class Installer:
if not superHost.downloadContentsFile(self.http):
Installer.notify.warning("couldn't read supermirror host %s" % superHost.hostUrl)
superHost = None
for name, version in self.requirements:
package = None
if superHost:
@ -243,7 +283,7 @@ class Installer:
if not package.downloadPackage(self.http):
Installer.notify.error(" -> %s failed for platform %s" % (package.packageName, package.platform))
continue
# Also install the 'images' package from the same host that p3dembed was downloaded from.
imageHost = host
if host.hostUrl != self.standalone.host.hostUrl:
@ -258,7 +298,7 @@ class Installer:
if superHost:
imagePackages += superHost.getPackages(name = "images")
imagePackages += imageHost.getPackages(name = "images")
for package in imagePackages:
package.installed = True # Hack not to let it unnecessarily install itself
packages.append(package)
@ -269,7 +309,7 @@ class Installer:
Installer.notify.error(" -> %s failed for platform %s" % (package.packageName, package.platform))
continue
break
# Remove the extracted files from the compressed archive, to save space.
vfs = VirtualFileSystem.getGlobalPtr()
for package in packages:
@ -277,7 +317,7 @@ class Installer:
archive = Filename(package.getPackageDir(), package.uncompressedArchive.filename)
if not archive.exists():
continue
mf = Multifile()
# Make sure that it isn't mounted before altering it, just to be safe
vfs.unmount(archive)
@ -285,7 +325,7 @@ class Installer:
if not mf.openReadWrite(archive):
Installer.notify.warning("Failed to open archive %s" % (archive))
continue
# We don't iterate over getNumSubfiles because we're
# removing subfiles while we're iterating over them.
subfiles = mf.getSubfileNames()
@ -294,10 +334,10 @@ class Installer:
if Filename(package.getPackageDir(), subfile).exists():
Installer.notify.debug("Removing already-extracted %s from multifile" % (subfile))
mf.removeSubfile(subfile)
# This seems essential for mf.close() not to crash later.
mf.repack()
# If we have no subfiles left, we can just remove the multifile.
if mf.getNumSubfiles() == 0:
Installer.notify.info("Removing empty archive %s" % (package.uncompressedArchive.filename))
@ -306,12 +346,12 @@ class Installer:
else:
mf.close()
os.chmod(archive.toOsSpecific(), 0400)
# Write out our own contents.xml file.
doc = TiXmlDocument()
decl = TiXmlDeclaration("1.0", "utf-8", "")
doc.InsertEndChild(decl)
xcontents = TiXmlElement("contents")
for package in packages:
xpackage = TiXmlElement('package')
@ -331,57 +371,85 @@ class Installer:
def buildAll(self, outputDir = "."):
""" Creates a (graphical) installer for every known platform.
Call this after you have set the desired parameters. """
platforms = set()
for package in self.standalone.host.getPackages(name = "p3dembed"):
platforms.add(package.platform)
if len(platforms) == 0:
Installer.notify.warning("No platforms found to build for!")
outputDir = Filename(outputDir + "/")
outputDir.makeDir()
for platform in platforms:
output = Filename(outputDir, platform + "/")
output.makeDir()
self.build(output, platform)
def build(self, output, platform = None):
""" Builds a (graphical) installer and stores it into the path
""" Builds (graphical) installers and stores it into the path
indicated by the 'output' argument. You can specify to build for
a different platform by altering the 'platform' argument.
If 'output' is a directory, the installer will be stored in it. """
if platform == None:
platform = PandaSystem.getPlatform()
if platform == "win32":
return self.buildNSIS(output, platform)
self.buildNSIS(output, platform)
return
elif "_" in platform:
os, arch = platform.split("_", 1)
if os == "linux":
return self.buildDEB(output, platform)
elif os == "osx":
return self.buildPKG(output, platform)
elif os == "freebsd":
return self.buildDEB(output, platform)
osname, arch = platform.split("_", 1)
if osname == "linux":
self.buildDEB(output, platform)
self.buildArch(output, platform)
return
elif osname == "osx":
self.buildPKG(output, platform)
return
Installer.notify.info("Ignoring unknown platform " + platform)
def __buildTempLinux(self, platform):
""" Builds a filesystem for Linux. Used so that buildDEB,
buildRPM and buildArch can share the same temp directory. """
tempdir = Filename(self.tempDir, platform)
if tempdir.exists():
return tempdir
tempdir.makeDir()
Filename(tempdir, "usr/bin/").makeDir()
if self.includeRequires:
extraTokens = {"host_dir" : "/usr/lib/" + self.shortname.lower()}
else:
extraTokens = {}
self.standalone.build(Filename(tempdir, "usr/bin/" + self.shortname.lower()), platform, extraTokens)
if not self.licensefile.empty():
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/LICENSE" % self.shortname.lower()).toOsSpecific())
if self.includeRequires:
hostDir = Filename(tempdir, "usr/lib/" + self.shortname.lower())
hostDir.makeDir()
self.installPackagesInto(hostDir, platform)
return tempdir
def buildDEB(self, output, platform):
""" Builds a .deb archive and stores it in the path indicated
by the 'output' argument. It will be built for the architecture
specified by the 'arch' argument.
If 'output' is a directory, the deb file will be stored in it. """
arch = platform.rsplit("_", 1)[-1]
output = Filename(output)
if output.isDirectory():
output = Filename(output, "%s_%s_%s.deb" % (self.shortname.lower(), self.version, arch))
Installer.notify.info("Creating %s..." % output)
modtime = int(time.time())
# Create a temporary directory and write the control file + launcher to it
tempdir = Filename.temporary("", self.shortname.lower() + "_deb_", "") + "/"
tempdir.makeDir()
controlfile = open(Filename(tempdir, "control").toOsSpecific(), "w")
# Create a control file in memory.
controlfile = StringIO()
print >>controlfile, "Package: %s" % self.shortname.lower()
print >>controlfile, "Version: %s" % self.version
print >>controlfile, "Maintainer: %s <%s>" % (self.authorname, self.authoremail)
@ -390,75 +458,117 @@ class Installer:
print >>controlfile, "Architecture: %s" % arch
print >>controlfile, "Description: %s" % self.fullname
print >>controlfile, "Depends: libc6, libgcc1, libstdc++6, libx11-6"
controlfile.close()
Filename(tempdir, "usr/bin/").makeDir()
if self.includeRequires:
self.standalone.tokens["host_dir"] = "/usr/lib/" + self.shortname.lower()
elif "host_dir" in self.standalone.tokens:
del self.standalone.tokens["host_dir"]
self.standalone.build(Filename(tempdir, "usr/bin/" + self.shortname.lower()), platform)
if not self.licensefile.empty():
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())
hostDir = Filename(tempdir, "usr/lib/" + self.shortname.lower())
hostDir.makeDir()
self.installPackagesInto(hostDir, platform)
controlinfo = TarInfoRoot("control")
controlinfo.mtime = modtime
controlinfo.size = controlfile.tell()
controlfile.seek(0)
# Create a control.tar.gz file in memory
controlfile = Filename(tempdir, "control")
controltargz = CachedFile()
controltarfile = tarfile.TarFile.gzopen("control.tar.gz", "w", controltargz, 9)
controltarfile.add(controlfile.toOsSpecific(), "control")
controltarfile.close()
controlfile.unlink()
# Create the data.tar.gz file in the temporary directory
datatargz = CachedFile()
datatarfile = tarfile.TarFile.gzopen("data.tar.gz", "w", datatargz, 9)
datatarfile.add(Filename(tempdir, "usr").toOsSpecific(), "/usr")
datatarfile.close()
# Create a temporary directory and write the launcher and dependencies to it.
tempdir = self.__buildTempLinux(platform)
# 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 output.exists():
output.unlink()
debfile = open(output.toOsSpecific(), "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")
# Write the control.tar.gz to the archive.
debfile.write("control.tar.gz %-12lu0 0 100644 %-10ld\x60\x0A" % (modtime, 0))
ctaroffs = debfile.tell()
ctarfile = tarfile.open("control.tar.gz", "w:gz", debfile, tarinfo = TarInfoRoot)
ctarfile.addfile(controlinfo, controlfile)
ctarfile.close()
ctarsize = debfile.tell() - ctaroffs
if (ctarsize & 1): debfile.write("\x0A")
# Write the data.tar.gz to the archive.
debfile.write("data.tar.gz %-12lu0 0 100644 %-10ld\x60\x0A" % (modtime, 0))
dtaroffs = debfile.tell()
dtarfile = tarfile.open("data.tar.gz", "w:gz", debfile, tarinfo = TarInfoRoot)
dtarfile.add(Filename(tempdir, "usr").toOsSpecific(), "/usr")
dtarfile.close()
dtarsize = debfile.tell() - dtaroffs
if (dtarsize & 1): debfile.write("\x0A")
# Write the correct sizes of the archives.
debfile.seek(ctaroffs - 12)
debfile.write("%-10ld" % ctarsize)
debfile.seek(dtaroffs - 12)
debfile.write("%-10ld" % dtarsize)
debfile.close()
try:
appRunner.rmtree(tempdir)
except:
try: shutil.rmtree(tempdir.toOsSpecific())
except: pass
return output
def buildArch(self, output, platform):
""" Builds an ArchLinux package and stores it in the path
indicated by the 'output' argument. It will be built for the
architecture specified by the 'arch' argument.
If 'output' is a directory, the deb file will be stored in it. """
arch = platform.rsplit("_", 1)[-1]
assert arch in ("i386", "amd64")
arch = {"i386" : "i686", "amd64" : "x86_64"}[arch]
pkgver = self.version + "-1"
output = Filename(output)
if output.isDirectory():
output = Filename(output, "%s-%s-%s.pkg.tar.gz" % (self.shortname.lower(), pkgver, arch))
Installer.notify.info("Creating %s..." % output)
modtime = int(time.time())
# Create a pkginfo file in memory.
pkginfo = StringIO()
print >>pkginfo, "# Generated using pdeploy"
print >>pkginfo, "# %s" % time.ctime(modtime)
print >>pkginfo, "pkgname = %s" % self.shortname.lower()
print >>pkginfo, "pkgver = %s" % pkgver
print >>pkginfo, "pkgdesc = %s" % self.fullname
print >>pkginfo, "builddate = %s" % modtime
print >>pkginfo, "packager = %s <%s>" % (self.authorname, self.authoremail)
print >>pkginfo, "arch = %s" % arch
if self.licensename != "":
print >>pkginfo, "license = %s" % self.licensename
pkginfoinfo = TarInfoRoot(".PKGINFO")
pkginfoinfo.mtime = modtime
pkginfoinfo.size = pkginfo.tell()
pkginfoinfo.seek(0)
# Create a temporary directory and write the launcher and dependencies to it.
tempdir = self.__buildTempLinux(platform)
# Create the actual package now.
pkgfile = tarfile.open(output.toOsSpecific(), "w:gz", tarinfo = TarInfoRoot)
pkgfile.addfile(pkginfoinfo, pkginfo)
pkgfile.add(tempdir.toOsSpecific(), "/")
if not self.licensefile.empty():
pkgfile.add(self.licensefile.toOsSpecific(), "/usr/share/licenses/%s/LICENSE" % self.shortname.lower())
pkgfile.close()
return output
def buildAPP(self, output, platform):
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 executable for the application bundle
exefile = Filename(output, "Contents/MacOS/" + self.shortname)
exefile.makeDir()
if self.includeRequires:
self.standalone.tokens["host_dir"] = "../Resources"
elif "host_dir" in self.standalone.tokens:
del self.standalone.tokens["host_dir"]
self.standalone.build(exefile, platform)
extraTokens = {"host_dir" : "../Resources"}
else:
extraTokens = {}
self.standalone.build(exefile, platform, extraTokens)
hostDir = Filename(output, "Contents/Resources/")
hostDir.makeDir()
self.installPackagesInto(hostDir, platform)
# 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.
@ -494,7 +604,7 @@ class Installer:
print >>plist, '</dict>'
print >>plist, '</plist>'
plist.close()
return output
def buildPKG(self, output, platform):
@ -504,7 +614,7 @@ class Installer:
if output.isDirectory():
output = Filename(output, "%s %s.pkg" % (self.fullname, self.version))
Installer.notify.info("Creating %s..." % output)
Filename(output, "Contents/Resources/en.lproj/").makeDir()
if self.licensefile:
shutil.copyfile(self.licensefile.toOsSpecific(), Filename(output, "Contents/Resources/License.txt").toOsSpecific())
@ -514,7 +624,7 @@ class Installer:
pkginfo = open(Filename(output, "Contents/Resources/package_version").toOsSpecific(), "w")
pkginfo.write("major: 1\nminor: 9")
pkginfo.close()
# Although it might make more sense to use Python's plistlib here,
# it is not available on non-OSX systems before Python 2.6.
plist = open(Filename(output, "Contents/Info.plist").toOsSpecific(), "w")
@ -560,7 +670,7 @@ class Installer:
plist.write('</dict>\n')
plist.write('</plist>\n')
plist.close()
plist = open(Filename(output, "Contents/Resources/TokenDefinitions.plist").toOsSpecific(), "w")
plist.write('<?xml version="1.0" encoding="UTF-8"?>\n')
plist.write('<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n')
@ -580,7 +690,7 @@ class Installer:
plist.write('</dict>\n')
plist.write('</plist>\n')
plist.close()
plist = open(Filename(output, "Contents/Resources/en.lproj/Description.plist").toOsSpecific(), "w")
plist.write('<?xml version="1.0" encoding="UTF-8"?>\n')
plist.write('<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n')
@ -593,27 +703,27 @@ class Installer:
plist.write('</dict>\n')
plist.write('</plist>\n')
plist.close()
if hasattr(tarfile, "PAX_FORMAT"):
archive = tarfile.open(Filename(output, "Contents/Archive.pax.gz").toOsSpecific(), "w:gz", format = tarfile.PAX_FORMAT)
archive = tarfile.open(Filename(output, "Contents/Archive.pax.gz").toOsSpecific(), "w:gz", format = tarfile.PAX_FORMAT, tarinfo = TarInfoRootOSX)
else:
archive = tarfile.open(Filename(output, "Contents/Archive.pax.gz").toOsSpecific(), "w:gz")
archive = tarfile.open(Filename(output, "Contents/Archive.pax.gz").toOsSpecific(), "w:gz", tarinfo = TarInfoRootOSX)
archive.add(appfn.toOsSpecific(), appname)
archive.close()
# Put the .pkg into a zipfile
archive = Filename(output.getDirname(), "%s %s.zip" % (self.fullname, self.version))
dir = Filename(output.getDirname())
dir.makeAbsolute()
zip = zipfile.ZipFile(archive.toOsSpecific(), 'w')
for root, dirs, files in os.walk(output.toOsSpecific()):
for root, dirs, files in self.os_walk(output.toOsSpecific()):
for name in files:
file = Filename.fromOsSpecific(os.path.join(root, name))
file.makeAbsolute()
file.makeRelativeTo(dir)
zip.write(os.path.join(root, name), str(file))
zip.close()
return output
def buildNSIS(self, output, platform):
@ -632,11 +742,11 @@ class Installer:
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
return None
output = Filename(output)
if output.isDirectory():
output = Filename(output, "%s %s.exe" % (self.fullname, self.version))
@ -647,15 +757,16 @@ class Installer:
exefile = Filename(Filename.getTempDirectory(), self.shortname + ".exe")
exefile.unlink()
if self.includeRequires:
self.standalone.tokens["host_dir"] = "."
elif "host_dir" in self.standalone.tokens:
del self.standalone.tokens["host_dir"]
self.standalone.build(exefile, platform)
extraTokens = {"host_dir" : "."}
else:
extraTokens = {}
self.standalone.build(exefile, platform, extraTokens)
# Temporary directory to store the hostdir in
hostDir = Filename.temporary("", self.shortname.lower() + "_exe_", "") + "/"
hostDir.makeDir()
self.installPackagesInto(hostDir, platform)
hostDir = Filename(self.tempDir, platform + "/")
if not hostDir.exists():
hostDir.makeDir()
self.installPackagesInto(hostDir, platform)
nsifile = Filename(Filename.getTempDirectory(), self.shortname + ".nsi")
nsifile.unlink()
@ -755,11 +866,6 @@ class Installer:
self.notify.warning("Unable to invoke NSIS command.")
nsifile.unlink()
try:
appRunner.rmtree(hostDir)
except:
try: shutil.rmtree(hostDir.toOsSpecific())
except: pass
return output
def os_walk(self, top):

View File

@ -218,7 +218,7 @@ elif not outputDir.isDirectory():
if deploy_mode == 'standalone':
s = Standalone(appFilename, tokens)
s.basename = shortname
if currentPlatform:
platform = PandaSystem.getPlatform()
if platform.startswith("win"):
@ -244,7 +244,7 @@ elif deploy_mode == 'installer':
i.authorname = authorname
i.authoremail = authoremail
i.includeRequires = includeRequires
if currentPlatform:
platform = PandaSystem.getPlatform()
if platform.startswith("win"):