Skip to content

Commit

Permalink
Bug 747597 - Port XULRunner's --install-app to Python. r=Mossop
Browse files Browse the repository at this point in the history
  • Loading branch information
ajvincent committed Oct 22, 2013
1 parent 7315bb9 commit 01b0e1b
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 1 deletion.
2 changes: 2 additions & 0 deletions xulrunner/app/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,5 @@ README_FILE = $(srcdir)/../README.xulrunner
libs::
$(INSTALL) $(IFLAGS1) $(README_FILE) $(DIST)/bin
$(INSTALL) $(IFLAGS1) $(topsrcdir)/LICENSE $(DIST)/bin
$(INSTALL) $(IFLAGS1) $(srcdir)/install_app.py $(DIST)/bin

212 changes: 212 additions & 0 deletions xulrunner/app/install_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
#!/usr/bin/env python

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.

# Min version of python is 2.7
import sys
if ((sys.version_info.major != 2) or (sys.version_info.minor < 7)):
raise Exception("You need to use Python version 2.7 or higher")

import os, shutil, re, zipfile
from ConfigParser import SafeConfigParser

# Platform-specific support
# see https://developer.mozilla.org/en/XULRunner/Deploying_XULRunner_1.8
if sys.platform.startswith('linux') or sys.platform == "win32":
def installApp(appLocation, installDir, appName, greDir):
zipApp, iniParser, appName = validateArguments(appLocation, installDir, appName, greDir)
if (zipApp):
zipApp.extractAll(installDir)
else:
shutil.copytree(appLocation, installDir)
shutil.copy2(os.path.join(greDir, xulrunnerStubName),
os.path.join(installDir, appName))
copyGRE(greDir, os.path.join(installDir, "xulrunner"))

if sys.platform.startswith('linux'):
xulrunnerStubName = "xulrunner-stub"

def makeAppName(leafName):
return leafName.lower()

elif sys.platform == "win32":
xulrunnerStubName = "xulrunner-stub.exe"

def makeAppName(leafName):
return leafName + ".exe"

elif sys.platform == "darwin":
xulrunnerStubName = "xulrunner"

def installApp(appLocation, installDir, appName, greDir):
zipApp, iniparser, appName = validateArguments(appLocation, installDir, appName, greDir)
installDir += "/" + appName + ".app"
resourcesDir = os.path.join(installDir, "Contents/Resources")
if (zipApp):
zipApp.extractAll(resourcesDir)
else:
shutil.copytree(appLocation, resourcesDir)
MacOSDir = os.path.join(installDir, "Contents/MacOS")
os.makedirs(MacOSDir)
shutil.copy2(os.path.join(greDir, xulrunnerStubName), MacOSDir)
copyGRE(greDir,
os.path.join(installDir, "Contents/Frameworks/XUL.framework"))

# Contents/Info.plist
contents = """<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleExecutable</key>
<string>xulrunner</string>
<key>NSAppleScriptEnabled</key>
<true/>
<key>CFBundleGetInfoString</key>
<string>$infoString</string>
<key>CFBundleName</key>
<string>$appName</string>
<key>CFBundleShortVersionString</key>
<string>$version</string>
<key>CFBundleVersion</key>
<string>$version.$buildID</string>
</dict>
</plist>
"""
version = iniparser.get("App", "Version")
buildID = iniparser.get("App", "BuildID")
infoString = appName + " " + version
contents = contents.replace("$infoString", infoString)
contents = contents.replace("$appName", appName)
contents = contents.replace("$version", version)
contents = contents.replace("$buildID", buildID)
infoPList = open(os.path.join(installDir, "Contents/Info.plist"), "w+b")
infoPList.write(contents)
infoPList.close()

def makeAppName(leafName):
return leafName

else:
# Implement xulrunnerStubName, installApp and makeAppName as above.
raise Exception("This operating system isn't supported for install_app.py yet!")
# End platform-specific support

def resolvePath(path):
return os.path.realpath(path)

def requireINIOption(iniparser, section, option):
if not (iniparser.has_option(section, option)):
raise Exception("application.ini must have a " + option + " option under the " + section + " section")

def checkAppINI(appLocation):
if (os.path.isdir(appLocation)):
zipApp = None
appINIPath = os.path.join(appLocation, "application.ini")
if not (os.path.isfile(appINIPath)):
raise Exception(appINIPath + " does not exist")
appINI = open(appINIPath)
elif (zipfile.is_zipfile(appLocation)):
zipApp = zipfile.ZipFile(appLocation)
if not ("application.ini" in zipApp.namelist()):
raise Exception("jar:" + appLocation + "!/application.ini does not exist")
appINI = zipApp.open("application.ini")
else:
raise Exception("appLocation must be a directory containing application.ini or a zip file with application.ini at its root")

# application.ini verification
iniparser = SafeConfigParser()
iniparser.readfp(appINI)
if not (iniparser.has_section("App")):
raise Exception("application.ini must have an App section")
if not (iniparser.has_section("Gecko")):
raise Exception("application.ini must have a Gecko section")
requireINIOption(iniparser, "App", "Name")
requireINIOption(iniparser, "App", "Version")
requireINIOption(iniparser, "App", "BuildID")
requireINIOption(iniparser, "App", "ID")
requireINIOption(iniparser, "Gecko", "MinVersion")

return zipApp, iniparser
pass

def copyGRE(greDir, targetDir):
shutil.copytree(greDir, targetDir, symlinks=True)

def validateArguments(appLocation, installDir, appName, greDir):
# application directory / zip verification
appLocation = resolvePath(appLocation)

# target directory
installDir = resolvePath(installDir)

if (os.path.exists(installDir)):
raise Exception("installDir must not exist: " + cmds.installDir)

greDir = resolvePath(greDir)
xulrunnerStubPath = os.path.isfile(os.path.join(greDir, xulrunnerStubName))
if not xulrunnerStubPath:
raise Exception("XULRunner stub executable not found: " + os.path.join(greDir, xulrunnerStubName))

# appName
zipApp, iniparser = checkAppINI(appLocation)
if not appName:
appName = iniparser.get("App", "Name")
appName = makeAppName(appName)
pattern = re.compile("[\\\/\:*?\"<>|\x00]")
if pattern.search(appName):
raise Exception("App name has illegal characters for at least one operating system")
return zipApp, iniparser, appName

def handleCommandLine():
import argparse

# Argument parsing.
parser = argparse.ArgumentParser(
description="XULRunner application installer",
usage="""install_app.py appLocation installDir greDir [--appName APPNAME]
install_app.py -h
install_app.py --version
"""
)
parser.add_argument(
"appLocation",
action="store",
help="The directory or ZIP file containing application.ini as a top-level child file"
)
parser.add_argument(
"installDir",
action="store",
help="The directory to install the application to"
)
parser.add_argument(
"--greDir",
action="store",
help="The directory containing the Gecko SDK (usually where this Python script lives)",
default=os.path.dirname(sys.argv[0])
)
parser.add_argument(
"--appName",
action="store",
help="The name of the application to install"
)
parser.add_argument("--version", action="version", version="%(prog)s 1.0")

# The command code.
cmds = parser.parse_args()
try:
installApp(cmds.appLocation, cmds.installDir, cmds.appName, cmds.greDir)
except exn:
shutil.rmtree(cmds.installDir)
raise exn
print cmds.appName + " application installed to " + cmds.installDir

if __name__ == '__main__':
handleCommandLine()
3 changes: 2 additions & 1 deletion xulrunner/installer/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ NO_PKG_FILES = \
regxpcom* \
$(NULL)

# We want xpcshell and run-mozilla in the SDK but not in the binary package.
# We want xpcshell, run-mozilla and install_app.py in the SDK but not in the binary package.
ifndef STAGE_SDK
NO_PKG_FILES += \
xpcshell* \
run-mozilla* \
install_app.py \
$(NULL)
endif

Expand Down

0 comments on commit 01b0e1b

Please sign in to comment.