xulrunner/app/install_app.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xulrunner/app/install_app.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,221 @@
     1.4 +#!/usr/bin/env python
     1.5 +
     1.6 +# This Source Code Form is subject to the terms of the Mozilla Public
     1.7 +# License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.8 +# You can obtain one at http://mozilla.org/MPL/2.0/.
     1.9 +
    1.10 +# Min version of python is 2.7
    1.11 +import sys
    1.12 +if ((sys.version_info.major != 2) or (sys.version_info.minor < 7)):
    1.13 +    raise Exception("You need to use Python version 2.7 or higher")
    1.14 +
    1.15 +import os, shutil, re, zipfile
    1.16 +from ConfigParser import SafeConfigParser
    1.17 +
    1.18 +# Platform-specific support
    1.19 +# see https://developer.mozilla.org/en/XULRunner/Deploying_XULRunner_1.8
    1.20 +if sys.platform.startswith('linux') or sys.platform == "win32":
    1.21 +    def installApp(appLocation, installDir, appName, greDir):
    1.22 +        zipApp, iniParser, appName = validateArguments(appLocation, installDir, appName, greDir)
    1.23 +        if (zipApp):
    1.24 +            zipApp.extractAll(installDir)
    1.25 +        else:
    1.26 +            shutil.copytree(appLocation, installDir)
    1.27 +        shutil.copy2(os.path.join(greDir, xulrunnerStubName),
    1.28 +                     os.path.join(installDir, appName))
    1.29 +        copyGRE(greDir, os.path.join(installDir, "xulrunner"))
    1.30 +
    1.31 +if sys.platform.startswith('linux'):
    1.32 +    xulrunnerStubName = "xulrunner-stub"
    1.33 +
    1.34 +    def makeAppName(leafName):
    1.35 +        return leafName.lower()
    1.36 +
    1.37 +elif sys.platform == "win32":
    1.38 +    xulrunnerStubName = "xulrunner-stub.exe"
    1.39 +
    1.40 +    def makeAppName(leafName):
    1.41 +        return leafName + ".exe"
    1.42 +
    1.43 +elif sys.platform == "darwin":
    1.44 +    xulrunnerStubName = "xulrunner"
    1.45 +
    1.46 +    def installApp(appLocation, installDir, appName, greDir):
    1.47 +        zipApp, iniparser, appName = validateArguments(appLocation, installDir, appName, greDir)
    1.48 +        installDir += "/" + appName + ".app"
    1.49 +        resourcesDir = os.path.join(installDir, "Contents/Resources")
    1.50 +        if (zipApp):
    1.51 +            zipApp.extractAll(resourcesDir)
    1.52 +        else:
    1.53 +            shutil.copytree(appLocation, resourcesDir)
    1.54 +        MacOSDir = os.path.join(installDir, "Contents/MacOS")
    1.55 +        os.makedirs(MacOSDir)
    1.56 +        shutil.copy2(os.path.join(greDir, xulrunnerStubName), MacOSDir)
    1.57 +        copyGRE(greDir,
    1.58 +                os.path.join(installDir, "Contents/Frameworks/XUL.framework"))
    1.59 +
    1.60 +        # Contents/Info.plist
    1.61 +        contents = """<?xml version="1.0" encoding="UTF-8"?>
    1.62 +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    1.63 +<plist version="1.0">
    1.64 +<dict>
    1.65 +<key>CFBundleInfoDictionaryVersion</key>
    1.66 +<string>6.0</string>
    1.67 +<key>CFBundlePackageType</key>
    1.68 +<string>APPL</string>
    1.69 +<key>CFBundleSignature</key>
    1.70 +<string>????</string>
    1.71 +<key>CFBundleExecutable</key>
    1.72 +<string>xulrunner</string>
    1.73 +<key>NSAppleScriptEnabled</key>
    1.74 +<true/>
    1.75 +<key>CFBundleGetInfoString</key>
    1.76 +<string>$infoString</string>
    1.77 +<key>CFBundleName</key>
    1.78 +<string>$appName</string>
    1.79 +<key>CFBundleShortVersionString</key>
    1.80 +<string>$version</string>
    1.81 +<key>CFBundleVersion</key>
    1.82 +<string>$version.$buildID</string>
    1.83 +<key>CFBundleIdentifier</key>
    1.84 +<string>$reverseVendor</string>
    1.85 +</dict>
    1.86 +</plist>
    1.87 +"""
    1.88 +        version = iniparser.get("App", "Version")
    1.89 +        buildID = iniparser.get("App", "BuildID")
    1.90 +        infoString = appName + " " + version
    1.91 +        reverseVendor = "com.vendor.unknown"
    1.92 +        appID = iniparser.get("App", "ID")
    1.93 +        colonIndex = appID.find("@") + 1
    1.94 +        if (colonIndex != 0):
    1.95 +            vendor = appID[colonIndex:]
    1.96 +            reverseVendor = ".".join(vendor.split(".")[::-1])
    1.97 +        contents = contents.replace("$infoString", infoString)
    1.98 +        contents = contents.replace("$appName", appName)
    1.99 +        contents = contents.replace("$version", version)
   1.100 +        contents = contents.replace("$buildID", buildID)
   1.101 +        contents = contents.replace("$reverseVendor", reverseVendor)
   1.102 +        infoPList = open(os.path.join(installDir, "Contents/Info.plist"), "w+b")
   1.103 +        infoPList.write(contents)
   1.104 +        infoPList.close()
   1.105 +
   1.106 +    def makeAppName(leafName):
   1.107 +        return leafName
   1.108 +
   1.109 +else:
   1.110 +    # Implement xulrunnerStubName, installApp and makeAppName as above.
   1.111 +    raise Exception("This operating system isn't supported for install_app.py yet!")
   1.112 +# End platform-specific support
   1.113 +
   1.114 +def resolvePath(path):
   1.115 +    return os.path.realpath(path)
   1.116 +
   1.117 +def requireINIOption(iniparser, section, option):
   1.118 +    if not (iniparser.has_option(section, option)):
   1.119 +        raise Exception("application.ini must have a " + option + " option under the " +  section + " section")
   1.120 +
   1.121 +def checkAppINI(appLocation):
   1.122 +    if (os.path.isdir(appLocation)):
   1.123 +        zipApp = None
   1.124 +        appINIPath = os.path.join(appLocation, "application.ini")
   1.125 +        if not (os.path.isfile(appINIPath)):
   1.126 +            raise Exception(appINIPath + " does not exist")
   1.127 +        appINI = open(appINIPath)
   1.128 +    elif (zipfile.is_zipfile(appLocation)):
   1.129 +        zipApp = zipfile.ZipFile(appLocation)
   1.130 +        if not ("application.ini" in zipApp.namelist()):
   1.131 +            raise Exception("jar:" + appLocation + "!/application.ini does not exist")
   1.132 +        appINI = zipApp.open("application.ini")
   1.133 +    else:
   1.134 +        raise Exception("appLocation must be a directory containing application.ini or a zip file with application.ini at its root")
   1.135 +
   1.136 +    # application.ini verification
   1.137 +    iniparser = SafeConfigParser()
   1.138 +    iniparser.readfp(appINI)
   1.139 +    if not (iniparser.has_section("App")):
   1.140 +        raise Exception("application.ini must have an App section")
   1.141 +    if not (iniparser.has_section("Gecko")):
   1.142 +        raise Exception("application.ini must have a Gecko section")
   1.143 +    requireINIOption(iniparser, "App", "Name")
   1.144 +    requireINIOption(iniparser, "App", "Version")
   1.145 +    requireINIOption(iniparser, "App", "BuildID")
   1.146 +    requireINIOption(iniparser, "App", "ID")
   1.147 +    requireINIOption(iniparser, "Gecko", "MinVersion")
   1.148 +
   1.149 +    return zipApp, iniparser
   1.150 +    pass
   1.151 +
   1.152 +def copyGRE(greDir, targetDir):
   1.153 +    shutil.copytree(greDir, targetDir, symlinks=True)
   1.154 +
   1.155 +def validateArguments(appLocation, installDir, appName, greDir):
   1.156 +    # application directory / zip verification
   1.157 +    appLocation = resolvePath(appLocation)
   1.158 +
   1.159 +    # target directory
   1.160 +    installDir = resolvePath(installDir)
   1.161 +
   1.162 +    if (os.path.exists(installDir)):
   1.163 +        raise Exception("installDir must not exist: " + cmds.installDir)
   1.164 +
   1.165 +    greDir = resolvePath(greDir)
   1.166 +    xulrunnerStubPath = os.path.isfile(os.path.join(greDir, xulrunnerStubName))
   1.167 +    if not xulrunnerStubPath:
   1.168 +        raise Exception("XULRunner stub executable not found: " + os.path.join(greDir, xulrunnerStubName))
   1.169 +
   1.170 +    # appName
   1.171 +    zipApp, iniparser = checkAppINI(appLocation)
   1.172 +    if not appName:
   1.173 +        appName = iniparser.get("App", "Name")
   1.174 +    appName = makeAppName(appName)
   1.175 +    pattern = re.compile("[\\\/\:*?\"<>|\x00]")
   1.176 +    if pattern.search(appName):
   1.177 +        raise Exception("App name has illegal characters for at least one operating system")
   1.178 +    return zipApp, iniparser, appName
   1.179 +
   1.180 +def handleCommandLine():
   1.181 +    import argparse
   1.182 +
   1.183 +    # Argument parsing.
   1.184 +    parser = argparse.ArgumentParser(
   1.185 +        description="XULRunner application installer",
   1.186 +        usage="""install_app.py appLocation installDir greDir [--appName APPNAME]
   1.187 +           install_app.py -h
   1.188 +           install_app.py --version
   1.189 +    """
   1.190 +    )
   1.191 +    parser.add_argument(
   1.192 +        "appLocation",
   1.193 +        action="store",
   1.194 +        help="The directory or ZIP file containing application.ini as a top-level child file"
   1.195 +    )
   1.196 +    parser.add_argument(
   1.197 +        "installDir",
   1.198 +        action="store",
   1.199 +        help="The directory to install the application to"
   1.200 +    )
   1.201 +    parser.add_argument(
   1.202 +        "--greDir",
   1.203 +        action="store",
   1.204 +        help="The directory containing the Gecko SDK (usually where this Python script lives)",
   1.205 +        default=os.path.dirname(sys.argv[0])
   1.206 +    )
   1.207 +    parser.add_argument(
   1.208 +        "--appName",
   1.209 +        action="store",
   1.210 +        help="The name of the application to install"
   1.211 +    )
   1.212 +    parser.add_argument("--version", action="version", version="%(prog)s 1.0")
   1.213 +
   1.214 +    # The command code.
   1.215 +    cmds = parser.parse_args()
   1.216 +    try:
   1.217 +        installApp(cmds.appLocation, cmds.installDir, cmds.appName, cmds.greDir)
   1.218 +    except exn:
   1.219 +        shutil.rmtree(cmds.installDir)
   1.220 +        raise exn
   1.221 +    print cmds.appName + " application installed to " + cmds.installDir
   1.222 +
   1.223 +if __name__ == '__main__':
   1.224 +    handleCommandLine()

mercurial