Installer package creation for OSX

This commit is contained in:
rdb 2009-10-22 12:59:27 +00:00
parent 04ef8343bf
commit 80196acac8

View File

@ -2,13 +2,15 @@
import os import os
import sys import sys
import shutil
import platform
from optparse import OptionParser from optparse import OptionParser
import subprocess import subprocess
import direct import direct
from pandac.PandaModules import * from pandac.PandaModules import *
usage = """ usage = """
This command invokes NSIS to construct a Windows installer for the This command creates a graphical installer for the
Panda3D plugin and runtime environment. Panda3D plugin and runtime environment.
%prog [opts]""" %prog [opts]"""
@ -20,11 +22,14 @@ parser.add_option('-n', '--short', dest = 'short_name',
parser.add_option('-N', '--long', dest = 'long_name', parser.add_option('-N', '--long', dest = 'long_name',
help = 'The product long name', help = 'The product long name',
default = 'Panda3D Game Engine') default = 'Panda3D Game Engine')
parser.add_option('-v', '--version', dest = 'version',
help = 'The product version',
default = PandaSystem.getVersionString())
parser.add_option('-p', '--publisher', dest = 'publisher', parser.add_option('-p', '--publisher', dest = 'publisher',
help = 'The name of the publisher', help = 'The name of the publisher',
default = 'Carnegie Mellon Entertainment Technology Center') default = 'Carnegie Mellon Entertainment Technology Center')
parser.add_option('-i', '--install', dest = 'install_dir', parser.add_option('-i', '--install', dest = 'install_dir',
help = "The install directory on the user's machine", help = "The install directory on the user's machine (Windows-only)",
default = '$PROGRAMFILES\\Panda3D') default = '$PROGRAMFILES\\Panda3D')
parser.add_option('-l', '--license', dest = 'license', parser.add_option('-l', '--license', dest = 'license',
help = 'A file containing the license or EULA text', help = 'A file containing the license or EULA text',
@ -35,15 +40,15 @@ parser.add_option('-w', '--website', dest = 'website',
parser.add_option('', '--start', dest = 'start', parser.add_option('', '--start', dest = 'start',
help = 'Specify this option to add a start menu', help = 'Specify this option to add a start menu',
action = 'store_false', default = False) action = 'store_false', default = False)
parser.add_option('', '--nsis', dest = 'nsis',
help = 'The directory containing NSIS',
default = None)
parser.add_option('', '--welcome_image', dest = 'welcome_image', parser.add_option('', '--welcome_image', dest = 'welcome_image',
help = 'The image to display on the installer', help = 'The image to display on the installer',
default = None) default = None)
parser.add_option('', '--install_icon', dest = 'install_icon', parser.add_option('', '--install_icon', dest = 'install_icon',
help = 'The icon to give to the installer', help = 'The icon to give to the installer',
default = None) default = None)
parser.add_option('', '--nsis', dest = 'nsis',
help = 'The directory containing NSIS',
default = None)
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
@ -51,34 +56,34 @@ this_dir = os.path.split(sys.argv[0])[0]
############################################################################## ##############################################################################
# #
# Locate the relevant trees. # Locate the relevant files.
# #
############################################################################## ##############################################################################
if not options.nsis or not options.license:
PANDA=None
for dir in sys.path:
if (dir != "") and os.path.exists(os.path.join(dir,"direct")) and os.path.exists(os.path.join(dir,"pandac")):
PANDA=os.path.abspath(dir)
if (PANDA is None):
sys.exit("Cannot locate the panda root directory in the python path (cannot locate directory containing direct and pandac).")
print "PANDA located at "+PANDA
if (os.path.exists(os.path.join(PANDA,"..","makepanda","makepanda.py"))) and (sys.platform != "win32" or os.path.exists(os.path.join(PANDA,"..","thirdparty","win-nsis","makensis.exe"))):
PSOURCE=os.path.abspath(os.path.join(PANDA,".."))
if (sys.platform == "win32"):
NSIS=os.path.abspath(os.path.join(PANDA,"..","thirdparty","win-nsis"))
else:
PSOURCE=PANDA
if (sys.platform == "win32"):
NSIS=os.path.join(PANDA,"nsis")
if not options.nsis: if not options.nsis:
options.nsis = NSIS if sys.platform == "win32":
if not options.license: makensis = None
options.license = os.path.join(PANDA, 'LICENSE') 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
options.nsis = makensis
else:
for p in os.defpath.split(":") + os.environ["PATH"].split(":"):
if os.path.isfile(os.path.join(p, "makensis")):
options.nsis = os.path.join(p, "makensis")
if not options.welcome_image: if not options.license:
import pandac
options.license = os.path.join(os.path.dirname(os.path.dirname(pandac.__file__)), "LICENSE")
if not os.path.isfile(options.license): options.license = None
if sys.platform == "win32" and not options.welcome_image:
filename = Filename('plugin_images/download.png') filename = Filename('plugin_images/download.png')
found = filename.resolveFilename(getModelPath().getValue()) found = filename.resolveFilename(getModelPath().getValue())
if not found: if not found:
@ -125,13 +130,30 @@ def parseDependenciesWindows(tempFile):
# At least we got some data. # At least we got some data.
return filenames return filenames
def parseDependenciesUnix(tempFile):
""" Reads the indicated temporary file, the output from
otool -XL or ldd, to determine the list of dll's this
executable file depends on. """
lines = open(tempFile.toOsSpecific(), 'rU').readlines()
filenames = []
for l in lines:
filenames.append(l.strip().split(' ', 1)[0])
return filenames
def addDependencies(path, pathname, file, pluginDependencies, dependentFiles): def addDependencies(path, pathname, file, pluginDependencies, dependentFiles):
""" Checks the named file for DLL dependencies, and adds any """ Checks the named file for DLL dependencies, and adds any
appropriate dependencies found into pluginDependencies and appropriate dependencies found into pluginDependencies and
dependentFiles. """ dependentFiles. """
tempFile = Filename.temporary('', 'p3d_', '.txt') tempFile = Filename.temporary('', 'p3d_', '.txt')
command = 'dumpbin /dependents "%s" >"%s"' % ( if sys.platform == "darwin":
command = 'otool -XL "%s" >"%s"'
elif sys.platform == "win32":
command = 'dumpbin /dependents "%s" >"%s"'
else:
command = 'ldd "%s" >"%s"'
command = command % (
pathname.toOsSpecific(), pathname.toOsSpecific(),
tempFile.toOsSpecific()) tempFile.toOsSpecific())
try: try:
@ -141,7 +163,10 @@ def addDependencies(path, pathname, file, pluginDependencies, dependentFiles):
filenames = None filenames = None
if tempFile.exists(): if tempFile.exists():
if sys.platform == "win32":
filenames = parseDependenciesWindows(tempFile) filenames = parseDependenciesWindows(tempFile)
else:
filenames = parseDependenciesUnix(tempFile)
tempFile.unlink() tempFile.unlink()
if filenames is None: if filenames is None:
sys.exit("Unable to determine dependencies from %s" % (pathname)) sys.exit("Unable to determine dependencies from %s" % (pathname))
@ -175,16 +200,28 @@ def makeInstaller():
pluginDependencies = {} pluginDependencies = {}
dependentFiles = {} dependentFiles = {}
# These are the four primary files that make up the # These are the primary files that make
# plugin/runtime. # up the plugin/runtime.
if sys.platform == "darwin":
npapi = 'nppanda3d.plugin'
panda3d = 'panda3d'
baseFiles = [npapi, panda3d]
else:
ocx = 'p3dactivex.ocx' ocx = 'p3dactivex.ocx'
npapi = 'nppanda3d.dll' npapi = 'nppanda3d.dll'
panda3d = 'panda3d.exe' panda3d = 'panda3d.exe'
panda3dw = 'panda3dw.exe' panda3dw = 'panda3dw.exe'
baseFiles = [ocx, npapi, panda3d, panda3dw]
path = DSearchPath() path = DSearchPath()
if 'PATH' in os.environ:
path.appendPath(os.environ['PATH']) path.appendPath(os.environ['PATH'])
for file in [ocx, npapi, panda3d, panda3dw]: if sys.platform != "win32" and 'LD_LIBRARY_PATH' in os.environ:
path.appendPath(os.environ['LD_LIBRARY_PATH'])
if sys.platform == "darwin" and 'DYLD_LIBRARY_PATH' in os.environ:
path.appendPath(os.environ['DYLD_LIBRARY_PATH'])
path.appendPath(os.defpath)
for file in baseFiles:
pathname = path.findFile(file) pathname = path.findFile(file)
if not pathname: if not pathname:
sys.exit("Couldn't find %s." % (file)) sys.exit("Couldn't find %s." % (file))
@ -192,9 +229,52 @@ def makeInstaller():
pluginFiles[file] = pathname.toOsSpecific() pluginFiles[file] = pathname.toOsSpecific()
pluginDependencies[file] = [] pluginDependencies[file] = []
if sys.platform == "win32":
# Also look for the dll's that these plugins reference. # Also look for the dll's that these plugins reference.
addDependencies(path, pathname, file, pluginDependencies, dependentFiles) addDependencies(path, pathname, file, pluginDependencies, dependentFiles)
if sys.platform == "darwin":
tmproot = Filename("/var/tmp/Panda3D Runtime/")
if tmproot.exists():
shutil.rmtree(tmproot.toOsSpecific())
tmproot.makeDir()
dst_npapi = Filename(tmproot, Filename("Library/Internet Plug-Ins", npapi))
dst_panda3d = Filename(tmproot, Filename("usr/bin", panda3d))
dst_npapi.makeDir()
dst_panda3d.makeDir()
shutil.copytree(pluginFiles[npapi], dst_npapi.toOsSpecific())
shutil.copyfile(pluginFiles[panda3d], dst_panda3d.toOsSpecific())
CMD = "/Developer/usr/bin/packagemaker"
CMD += ' --id org.panda3d.runtime' #TODO: make this customizable
CMD += ' --version "%s"' % options.version
CMD += ' --title "%s"' % options.long_name
CMD += ' --out p3d-setup.pkg'
CMD += ' --target %s' % platform.mac_ver()[0][:4]
CMD += ' --domain system'
CMD += ' --root "%s"' % tmproot.toOsSpecific()
print ""
print CMD
subprocess.call(CMD, shell = True)
shutil.rmtree(tmproot.toOsSpecific())
# Pack the .pkg into a .dmg
tmproot.makeDir()
shutil.copyfile("p3d-setup.pkg", Filename(tmproot, "p3d-setup.pkg").toOsSpecific())
tmpdmg = Filename.temporary("", "p3d-setup", "").toOsSpecific() + ".dmg"
CMD = 'hdiutil create "%s" -srcfolder "%s"' % (tmpdmg, tmproot)
print ""
print CMD
subprocess.call(CMD, shell = True)
shutil.rmtree(tmproot.toOsSpecific())
# Compress the .dmg (and make it read-only)
CMD = 'hdiutil convert "%s" -format UDBZ -o "p3d-setup.dmg"' % tmpdmg
print ""
print CMD
subprocess.call(CMD, shell = True)
else:
welcomeBitmap = None welcomeBitmap = None
if options.welcome_image: if options.welcome_image:
# Convert the image from its current format to a bmp file, for NSIS. # Convert the image from its current format to a bmp file, for NSIS.