Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | #!/usr/bin/env python |
michael@0 | 2 | |
michael@0 | 3 | # This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 5 | # You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 6 | |
michael@0 | 7 | # Min version of python is 2.7 |
michael@0 | 8 | import sys |
michael@0 | 9 | if ((sys.version_info.major != 2) or (sys.version_info.minor < 7)): |
michael@0 | 10 | raise Exception("You need to use Python version 2.7 or higher") |
michael@0 | 11 | |
michael@0 | 12 | import os, shutil, re, zipfile |
michael@0 | 13 | from ConfigParser import SafeConfigParser |
michael@0 | 14 | |
michael@0 | 15 | # Platform-specific support |
michael@0 | 16 | # see https://developer.mozilla.org/en/XULRunner/Deploying_XULRunner_1.8 |
michael@0 | 17 | if sys.platform.startswith('linux') or sys.platform == "win32": |
michael@0 | 18 | def installApp(appLocation, installDir, appName, greDir): |
michael@0 | 19 | zipApp, iniParser, appName = validateArguments(appLocation, installDir, appName, greDir) |
michael@0 | 20 | if (zipApp): |
michael@0 | 21 | zipApp.extractAll(installDir) |
michael@0 | 22 | else: |
michael@0 | 23 | shutil.copytree(appLocation, installDir) |
michael@0 | 24 | shutil.copy2(os.path.join(greDir, xulrunnerStubName), |
michael@0 | 25 | os.path.join(installDir, appName)) |
michael@0 | 26 | copyGRE(greDir, os.path.join(installDir, "xulrunner")) |
michael@0 | 27 | |
michael@0 | 28 | if sys.platform.startswith('linux'): |
michael@0 | 29 | xulrunnerStubName = "xulrunner-stub" |
michael@0 | 30 | |
michael@0 | 31 | def makeAppName(leafName): |
michael@0 | 32 | return leafName.lower() |
michael@0 | 33 | |
michael@0 | 34 | elif sys.platform == "win32": |
michael@0 | 35 | xulrunnerStubName = "xulrunner-stub.exe" |
michael@0 | 36 | |
michael@0 | 37 | def makeAppName(leafName): |
michael@0 | 38 | return leafName + ".exe" |
michael@0 | 39 | |
michael@0 | 40 | elif sys.platform == "darwin": |
michael@0 | 41 | xulrunnerStubName = "xulrunner" |
michael@0 | 42 | |
michael@0 | 43 | def installApp(appLocation, installDir, appName, greDir): |
michael@0 | 44 | zipApp, iniparser, appName = validateArguments(appLocation, installDir, appName, greDir) |
michael@0 | 45 | installDir += "/" + appName + ".app" |
michael@0 | 46 | resourcesDir = os.path.join(installDir, "Contents/Resources") |
michael@0 | 47 | if (zipApp): |
michael@0 | 48 | zipApp.extractAll(resourcesDir) |
michael@0 | 49 | else: |
michael@0 | 50 | shutil.copytree(appLocation, resourcesDir) |
michael@0 | 51 | MacOSDir = os.path.join(installDir, "Contents/MacOS") |
michael@0 | 52 | os.makedirs(MacOSDir) |
michael@0 | 53 | shutil.copy2(os.path.join(greDir, xulrunnerStubName), MacOSDir) |
michael@0 | 54 | copyGRE(greDir, |
michael@0 | 55 | os.path.join(installDir, "Contents/Frameworks/XUL.framework")) |
michael@0 | 56 | |
michael@0 | 57 | # Contents/Info.plist |
michael@0 | 58 | contents = """<?xml version="1.0" encoding="UTF-8"?> |
michael@0 | 59 | <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
michael@0 | 60 | <plist version="1.0"> |
michael@0 | 61 | <dict> |
michael@0 | 62 | <key>CFBundleInfoDictionaryVersion</key> |
michael@0 | 63 | <string>6.0</string> |
michael@0 | 64 | <key>CFBundlePackageType</key> |
michael@0 | 65 | <string>APPL</string> |
michael@0 | 66 | <key>CFBundleSignature</key> |
michael@0 | 67 | <string>????</string> |
michael@0 | 68 | <key>CFBundleExecutable</key> |
michael@0 | 69 | <string>xulrunner</string> |
michael@0 | 70 | <key>NSAppleScriptEnabled</key> |
michael@0 | 71 | <true/> |
michael@0 | 72 | <key>CFBundleGetInfoString</key> |
michael@0 | 73 | <string>$infoString</string> |
michael@0 | 74 | <key>CFBundleName</key> |
michael@0 | 75 | <string>$appName</string> |
michael@0 | 76 | <key>CFBundleShortVersionString</key> |
michael@0 | 77 | <string>$version</string> |
michael@0 | 78 | <key>CFBundleVersion</key> |
michael@0 | 79 | <string>$version.$buildID</string> |
michael@0 | 80 | <key>CFBundleIdentifier</key> |
michael@0 | 81 | <string>$reverseVendor</string> |
michael@0 | 82 | </dict> |
michael@0 | 83 | </plist> |
michael@0 | 84 | """ |
michael@0 | 85 | version = iniparser.get("App", "Version") |
michael@0 | 86 | buildID = iniparser.get("App", "BuildID") |
michael@0 | 87 | infoString = appName + " " + version |
michael@0 | 88 | reverseVendor = "com.vendor.unknown" |
michael@0 | 89 | appID = iniparser.get("App", "ID") |
michael@0 | 90 | colonIndex = appID.find("@") + 1 |
michael@0 | 91 | if (colonIndex != 0): |
michael@0 | 92 | vendor = appID[colonIndex:] |
michael@0 | 93 | reverseVendor = ".".join(vendor.split(".")[::-1]) |
michael@0 | 94 | contents = contents.replace("$infoString", infoString) |
michael@0 | 95 | contents = contents.replace("$appName", appName) |
michael@0 | 96 | contents = contents.replace("$version", version) |
michael@0 | 97 | contents = contents.replace("$buildID", buildID) |
michael@0 | 98 | contents = contents.replace("$reverseVendor", reverseVendor) |
michael@0 | 99 | infoPList = open(os.path.join(installDir, "Contents/Info.plist"), "w+b") |
michael@0 | 100 | infoPList.write(contents) |
michael@0 | 101 | infoPList.close() |
michael@0 | 102 | |
michael@0 | 103 | def makeAppName(leafName): |
michael@0 | 104 | return leafName |
michael@0 | 105 | |
michael@0 | 106 | else: |
michael@0 | 107 | # Implement xulrunnerStubName, installApp and makeAppName as above. |
michael@0 | 108 | raise Exception("This operating system isn't supported for install_app.py yet!") |
michael@0 | 109 | # End platform-specific support |
michael@0 | 110 | |
michael@0 | 111 | def resolvePath(path): |
michael@0 | 112 | return os.path.realpath(path) |
michael@0 | 113 | |
michael@0 | 114 | def requireINIOption(iniparser, section, option): |
michael@0 | 115 | if not (iniparser.has_option(section, option)): |
michael@0 | 116 | raise Exception("application.ini must have a " + option + " option under the " + section + " section") |
michael@0 | 117 | |
michael@0 | 118 | def checkAppINI(appLocation): |
michael@0 | 119 | if (os.path.isdir(appLocation)): |
michael@0 | 120 | zipApp = None |
michael@0 | 121 | appINIPath = os.path.join(appLocation, "application.ini") |
michael@0 | 122 | if not (os.path.isfile(appINIPath)): |
michael@0 | 123 | raise Exception(appINIPath + " does not exist") |
michael@0 | 124 | appINI = open(appINIPath) |
michael@0 | 125 | elif (zipfile.is_zipfile(appLocation)): |
michael@0 | 126 | zipApp = zipfile.ZipFile(appLocation) |
michael@0 | 127 | if not ("application.ini" in zipApp.namelist()): |
michael@0 | 128 | raise Exception("jar:" + appLocation + "!/application.ini does not exist") |
michael@0 | 129 | appINI = zipApp.open("application.ini") |
michael@0 | 130 | else: |
michael@0 | 131 | raise Exception("appLocation must be a directory containing application.ini or a zip file with application.ini at its root") |
michael@0 | 132 | |
michael@0 | 133 | # application.ini verification |
michael@0 | 134 | iniparser = SafeConfigParser() |
michael@0 | 135 | iniparser.readfp(appINI) |
michael@0 | 136 | if not (iniparser.has_section("App")): |
michael@0 | 137 | raise Exception("application.ini must have an App section") |
michael@0 | 138 | if not (iniparser.has_section("Gecko")): |
michael@0 | 139 | raise Exception("application.ini must have a Gecko section") |
michael@0 | 140 | requireINIOption(iniparser, "App", "Name") |
michael@0 | 141 | requireINIOption(iniparser, "App", "Version") |
michael@0 | 142 | requireINIOption(iniparser, "App", "BuildID") |
michael@0 | 143 | requireINIOption(iniparser, "App", "ID") |
michael@0 | 144 | requireINIOption(iniparser, "Gecko", "MinVersion") |
michael@0 | 145 | |
michael@0 | 146 | return zipApp, iniparser |
michael@0 | 147 | pass |
michael@0 | 148 | |
michael@0 | 149 | def copyGRE(greDir, targetDir): |
michael@0 | 150 | shutil.copytree(greDir, targetDir, symlinks=True) |
michael@0 | 151 | |
michael@0 | 152 | def validateArguments(appLocation, installDir, appName, greDir): |
michael@0 | 153 | # application directory / zip verification |
michael@0 | 154 | appLocation = resolvePath(appLocation) |
michael@0 | 155 | |
michael@0 | 156 | # target directory |
michael@0 | 157 | installDir = resolvePath(installDir) |
michael@0 | 158 | |
michael@0 | 159 | if (os.path.exists(installDir)): |
michael@0 | 160 | raise Exception("installDir must not exist: " + cmds.installDir) |
michael@0 | 161 | |
michael@0 | 162 | greDir = resolvePath(greDir) |
michael@0 | 163 | xulrunnerStubPath = os.path.isfile(os.path.join(greDir, xulrunnerStubName)) |
michael@0 | 164 | if not xulrunnerStubPath: |
michael@0 | 165 | raise Exception("XULRunner stub executable not found: " + os.path.join(greDir, xulrunnerStubName)) |
michael@0 | 166 | |
michael@0 | 167 | # appName |
michael@0 | 168 | zipApp, iniparser = checkAppINI(appLocation) |
michael@0 | 169 | if not appName: |
michael@0 | 170 | appName = iniparser.get("App", "Name") |
michael@0 | 171 | appName = makeAppName(appName) |
michael@0 | 172 | pattern = re.compile("[\\\/\:*?\"<>|\x00]") |
michael@0 | 173 | if pattern.search(appName): |
michael@0 | 174 | raise Exception("App name has illegal characters for at least one operating system") |
michael@0 | 175 | return zipApp, iniparser, appName |
michael@0 | 176 | |
michael@0 | 177 | def handleCommandLine(): |
michael@0 | 178 | import argparse |
michael@0 | 179 | |
michael@0 | 180 | # Argument parsing. |
michael@0 | 181 | parser = argparse.ArgumentParser( |
michael@0 | 182 | description="XULRunner application installer", |
michael@0 | 183 | usage="""install_app.py appLocation installDir greDir [--appName APPNAME] |
michael@0 | 184 | install_app.py -h |
michael@0 | 185 | install_app.py --version |
michael@0 | 186 | """ |
michael@0 | 187 | ) |
michael@0 | 188 | parser.add_argument( |
michael@0 | 189 | "appLocation", |
michael@0 | 190 | action="store", |
michael@0 | 191 | help="The directory or ZIP file containing application.ini as a top-level child file" |
michael@0 | 192 | ) |
michael@0 | 193 | parser.add_argument( |
michael@0 | 194 | "installDir", |
michael@0 | 195 | action="store", |
michael@0 | 196 | help="The directory to install the application to" |
michael@0 | 197 | ) |
michael@0 | 198 | parser.add_argument( |
michael@0 | 199 | "--greDir", |
michael@0 | 200 | action="store", |
michael@0 | 201 | help="The directory containing the Gecko SDK (usually where this Python script lives)", |
michael@0 | 202 | default=os.path.dirname(sys.argv[0]) |
michael@0 | 203 | ) |
michael@0 | 204 | parser.add_argument( |
michael@0 | 205 | "--appName", |
michael@0 | 206 | action="store", |
michael@0 | 207 | help="The name of the application to install" |
michael@0 | 208 | ) |
michael@0 | 209 | parser.add_argument("--version", action="version", version="%(prog)s 1.0") |
michael@0 | 210 | |
michael@0 | 211 | # The command code. |
michael@0 | 212 | cmds = parser.parse_args() |
michael@0 | 213 | try: |
michael@0 | 214 | installApp(cmds.appLocation, cmds.installDir, cmds.appName, cmds.greDir) |
michael@0 | 215 | except exn: |
michael@0 | 216 | shutil.rmtree(cmds.installDir) |
michael@0 | 217 | raise exn |
michael@0 | 218 | print cmds.appName + " application installed to " + cmds.installDir |
michael@0 | 219 | |
michael@0 | 220 | if __name__ == '__main__': |
michael@0 | 221 | handleCommandLine() |