| OLD | NEW |
| 1 # coding: utf-8 | 1 # coding: utf-8 |
| 2 | 2 |
| 3 # This file is part of the Adblock Plus build tools, | 3 # This file is part of the Adblock Plus build tools, |
| 4 # Copyright (C) 2006-2012 Eyeo GmbH | 4 # Copyright (C) 2006-2012 Eyeo GmbH |
| 5 # | 5 # |
| 6 # Adblock Plus is free software: you can redistribute it and/or modify | 6 # Adblock Plus is free software: you can redistribute it and/or modify |
| 7 # it under the terms of the GNU General Public License version 3 as | 7 # it under the terms of the GNU General Public License version 3 as |
| 8 # published by the Free Software Foundation. | 8 # published by the Free Software Foundation. |
| 9 # | 9 # |
| 10 # Adblock Plus is distributed in the hope that it will be useful, | 10 # Adblock Plus is distributed in the hope that it will be useful, |
| 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 # GNU General Public License for more details. | 13 # GNU General Public License for more details. |
| 14 # | 14 # |
| 15 # You should have received a copy of the GNU General Public License | 15 # You should have received a copy of the GNU General Public License |
| 16 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 16 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
| 17 | 17 |
| 18 import os, sys, re, subprocess, jinja2, buildtools, codecs, hashlib, base64, shu
til, urllib, json | 18 import os, sys, re, hashlib, base64, urllib, json |
| 19 from ConfigParser import SafeConfigParser | 19 from ConfigParser import SafeConfigParser |
| 20 from StringIO import StringIO | 20 from StringIO import StringIO |
| 21 from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED | 21 from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED |
| 22 import xml.dom.minidom as minidom | 22 import xml.dom.minidom as minidom |
| 23 import buildtools.localeTools as localeTools | 23 import buildtools.localeTools as localeTools |
| 24 | 24 |
| 25 from packager import getDefaultFileName, readMetadata, getBuildVersion, getTempl
ate |
| 26 |
| 25 KNOWN_APPS = { | 27 KNOWN_APPS = { |
| 26 'conkeror': '{a79fe89b-6662-4ff4-8e88-09950ad4dfde}', | 28 'conkeror': '{a79fe89b-6662-4ff4-8e88-09950ad4dfde}', |
| 27 'emusic': 'dlm@emusic.com', | 29 'emusic': 'dlm@emusic.com', |
| 28 'fennec': '{a23983c0-fd0e-11dc-95ff-0800200c9a66}', | 30 'fennec': '{a23983c0-fd0e-11dc-95ff-0800200c9a66}', |
| 29 'fennec2': '{aa3c5121-dab2-40e2-81ca-7ea25febc110}', | 31 'fennec2': '{aa3c5121-dab2-40e2-81ca-7ea25febc110}', |
| 30 'firefox': '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}', | 32 'firefox': '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}', |
| 31 'midbrowser': '{aa5ca914-c309-495d-91cf-3141bbb04115}', | 33 'midbrowser': '{aa5ca914-c309-495d-91cf-3141bbb04115}', |
| 32 'prism': 'prism@developer.mozilla.org', | 34 'prism': 'prism@developer.mozilla.org', |
| 33 'seamonkey': '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}', | 35 'seamonkey': '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}', |
| 34 'songbird': 'songbird@songbirdnest.com', | 36 'songbird': 'songbird@songbirdnest.com', |
| 35 'thunderbird': '{3550f703-e582-4d05-9a08-453d09bdfdc6}', | 37 'thunderbird': '{3550f703-e582-4d05-9a08-453d09bdfdc6}', |
| 36 'toolkit': 'toolkit@mozilla.org', | 38 'toolkit': 'toolkit@mozilla.org', |
| 37 } | 39 } |
| 38 | 40 |
| 39 defaultLocale = 'en-US' | 41 defaultLocale = 'en-US' |
| 40 | 42 |
| 41 def getDefaultFileName(baseDir, metadata, version, ext='xpi'): | |
| 42 return os.path.join(baseDir, '%s-%s.%s' % (metadata.get('general', 'basename')
, version, ext)) | |
| 43 | |
| 44 def getMetadataPath(baseDir): | |
| 45 return os.path.join(baseDir, 'metadata') | |
| 46 | |
| 47 def getChromeDir(baseDir): | 43 def getChromeDir(baseDir): |
| 48 return os.path.join(baseDir, 'chrome') | 44 return os.path.join(baseDir, 'chrome') |
| 49 | 45 |
| 50 def getLocalesDir(baseDir): | 46 def getLocalesDir(baseDir): |
| 51 return os.path.join(getChromeDir(baseDir), 'locale') | 47 return os.path.join(getChromeDir(baseDir), 'locale') |
| 52 | 48 |
| 53 def getChromeSubdirs(baseDir, locales): | 49 def getChromeSubdirs(baseDir, locales): |
| 54 result = {} | 50 result = {} |
| 55 chromeDir = getChromeDir(baseDir) | 51 chromeDir = getChromeDir(baseDir) |
| 56 for subdir in ('content', 'skin'): | 52 for subdir in ('content', 'skin'): |
| (...skipping 26 matching lines...) Expand all Loading... |
| 83 return False | 79 return False |
| 84 return True | 80 return True |
| 85 | 81 |
| 86 def getLocales(baseDir, includeIncomplete=False): | 82 def getLocales(baseDir, includeIncomplete=False): |
| 87 global defaultLocale | 83 global defaultLocale |
| 88 localesDir = getLocalesDir(baseDir) | 84 localesDir = getLocalesDir(baseDir) |
| 89 locales = filter(lambda dir: isValidLocale(localesDir, dir, includeIncomplete
), os.listdir(localesDir)) | 85 locales = filter(lambda dir: isValidLocale(localesDir, dir, includeIncomplete
), os.listdir(localesDir)) |
| 90 locales.sort(key=lambda x: '!' if x == defaultLocale else x) | 86 locales.sort(key=lambda x: '!' if x == defaultLocale else x) |
| 91 return locales | 87 return locales |
| 92 | 88 |
| 93 def getBuildNum(baseDir): | |
| 94 try: | |
| 95 (result, dummy) = subprocess.Popen(['hg', 'id', '-n'], stdout=subprocess.PIP
E).communicate() | |
| 96 return re.sub(r'\W', '', result) | |
| 97 except Exception: | |
| 98 return '0' | |
| 99 | |
| 100 def readMetadata(baseDir): | |
| 101 metadata = SafeConfigParser() | |
| 102 metadata.optionxform = str | |
| 103 file = codecs.open(getMetadataPath(baseDir), 'rb', encoding='utf-8') | |
| 104 metadata.readfp(file) | |
| 105 file.close() | |
| 106 return metadata | |
| 107 | |
| 108 def processFile(path, data, params): | 89 def processFile(path, data, params): |
| 109 if not re.search(r'\.(manifest|xul|jsm?|xml|xhtml|rdf|dtd|properties|css)$', p
ath): | 90 if not re.search(r'\.(manifest|xul|jsm?|xml|xhtml|rdf|dtd|properties|css)$', p
ath): |
| 110 return data | 91 return data |
| 111 | 92 |
| 112 data = re.sub(r'\r', '', data) | 93 data = re.sub(r'\r', '', data) |
| 113 data = data.replace('{{BUILD}}', params['buildNum']) | |
| 114 data = data.replace('{{VERSION}}', params['version']) | 94 data = data.replace('{{VERSION}}', params['version']) |
| 115 | 95 |
| 116 whitespaceRegExp = re.compile(r'^( )+', re.M) | 96 whitespaceRegExp = re.compile(r'^( )+', re.M) |
| 117 data = re.sub(whitespaceRegExp, lambda match: '\t' * (len(match.group(0)) / 2)
, data) | 97 data = re.sub(whitespaceRegExp, lambda match: '\t' * (len(match.group(0)) / 2)
, data) |
| 118 | 98 |
| 119 if path.endswith('.manifest') and data.find('{{LOCALE}}') >= 0: | 99 if path.endswith('.manifest') and data.find('{{LOCALE}}') >= 0: |
| 120 localesRegExp = re.compile(r'^(.*?){{LOCALE}}(.*?){{LOCALE}}(.*)$', re.M) | 100 localesRegExp = re.compile(r'^(.*?){{LOCALE}}(.*?){{LOCALE}}(.*)$', re.M) |
| 121 replacement = '\n'.join(map(lambda locale: r'\1%s\2%s\3' % (locale, locale),
params['locales'])) | 101 replacement = '\n'.join(map(lambda locale: r'\1%s\2%s\3' % (locale, locale),
params['locales'])) |
| 122 data = re.sub(localesRegExp, replacement, data) | 102 data = re.sub(localesRegExp, replacement, data) |
| 123 | 103 |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 173 | 153 |
| 174 def initTranslators(localeMetadata): | 154 def initTranslators(localeMetadata): |
| 175 for locale in localeMetadata.itervalues(): | 155 for locale in localeMetadata.itervalues(): |
| 176 if 'translator' in locale: | 156 if 'translator' in locale: |
| 177 locale['translators'] = sorted(map(lambda t: t.strip(), locale['translator
'].split(',')), key=unicode.lower) | 157 locale['translators'] = sorted(map(lambda t: t.strip(), locale['translator
'].split(',')), key=unicode.lower) |
| 178 else: | 158 else: |
| 179 locale['translators'] = [] | 159 locale['translators'] = [] |
| 180 | 160 |
| 181 def createManifest(baseDir, params): | 161 def createManifest(baseDir, params): |
| 182 global KNOWN_APPS, defaultLocale | 162 global KNOWN_APPS, defaultLocale |
| 183 env = jinja2.Environment(loader=jinja2.FileSystemLoader(buildtools.__path__[0]
), autoescape=True, extensions=['jinja2.ext.autoescape']) | 163 template = getTemplate('install.rdf.tmpl', autoEscape=True) |
| 184 template = env.get_template('install.rdf.tmpl') | |
| 185 templateData = dict(params) | 164 templateData = dict(params) |
| 186 templateData['localeMetadata'] = readLocaleMetadata(baseDir, params['locales']
) | 165 templateData['localeMetadata'] = readLocaleMetadata(baseDir, params['locales']
) |
| 187 initTranslators(templateData['localeMetadata']) | 166 initTranslators(templateData['localeMetadata']) |
| 188 templateData['KNOWN_APPS'] = KNOWN_APPS | 167 templateData['KNOWN_APPS'] = KNOWN_APPS |
| 189 templateData['defaultLocale'] = defaultLocale | 168 templateData['defaultLocale'] = defaultLocale |
| 190 return template.render(templateData).encode('utf-8') | 169 return template.render(templateData).encode('utf-8') |
| 191 | 170 |
| 192 def readFile(files, params, path, name): | 171 def readFile(files, params, path, name): |
| 193 ignoredFiles = getIgnoredFiles(params) | 172 ignoredFiles = getIgnoredFiles(params) |
| 194 if os.path.isdir(path): | 173 if os.path.isdir(path): |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 267 elif name.endswith('.xul'): | 246 elif name.endswith('.xul'): |
| 268 match = re.search(r'<(?:window|dialog)\s[^>]*\bwindowtype="([^">]+)"', con
tent) | 247 match = re.search(r'<(?:window|dialog)\s[^>]*\bwindowtype="([^">]+)"', con
tent) |
| 269 if match: | 248 if match: |
| 270 templateData['chromeWindows'].append(match.group(1)) | 249 templateData['chromeWindows'].append(match.group(1)) |
| 271 | 250 |
| 272 while True: | 251 while True: |
| 273 missing = [] | 252 missing = [] |
| 274 for module in templateData['requires']: | 253 for module in templateData['requires']: |
| 275 moduleFile = 'lib/' + module + '.js' | 254 moduleFile = 'lib/' + module + '.js' |
| 276 if not moduleFile in files: | 255 if not moduleFile in files: |
| 256 import buildtools |
| 277 path = os.path.join(buildtools.__path__[0], moduleFile) | 257 path = os.path.join(buildtools.__path__[0], moduleFile) |
| 278 if os.path.exists(path): | 258 if os.path.exists(path): |
| 279 missing.append((path, moduleFile)) | 259 missing.append((path, moduleFile)) |
| 280 if not len(missing): | 260 if not len(missing): |
| 281 break | 261 break |
| 282 for path, moduleFile in missing: | 262 for path, moduleFile in missing: |
| 283 readFile(files, params, path, moduleFile) | 263 readFile(files, params, path, moduleFile) |
| 284 checkScript(moduleFile) | 264 checkScript(moduleFile) |
| 285 | 265 |
| 286 env = jinja2.Environment(loader=jinja2.FileSystemLoader(buildtools.__path__[0]
)) | 266 template = getTemplate('bootstrap.js.tmpl') |
| 287 env.filters['json'] = json.dumps | |
| 288 template = env.get_template('bootstrap.js.tmpl') | |
| 289 files['bootstrap.js'] = processFile('bootstrap.js', template.render(templateDa
ta).encode('utf-8'), params) | 267 files['bootstrap.js'] = processFile('bootstrap.js', template.render(templateDa
ta).encode('utf-8'), params) |
| 290 | 268 |
| 291 def signFiles(files, keyFile): | 269 def signFiles(files, keyFile): |
| 292 import M2Crypto | 270 import M2Crypto |
| 293 manifest = [] | 271 manifest = [] |
| 294 signature = [] | 272 signature = [] |
| 295 | 273 |
| 296 def getDigest(data): | 274 def getDigest(data): |
| 297 md5 = hashlib.md5() | 275 md5 = hashlib.md5() |
| 298 md5.update(data) | 276 md5.update(data) |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 339 | 317 |
| 340 def writeXPI(files, outFile): | 318 def writeXPI(files, outFile): |
| 341 zip = ZipFile(outFile, 'w', ZIP_DEFLATED) | 319 zip = ZipFile(outFile, 'w', ZIP_DEFLATED) |
| 342 names = files.keys() | 320 names = files.keys() |
| 343 names.sort(key=lambda x: '!' if x == 'META-INF/zigbert.rsa' else x) | 321 names.sort(key=lambda x: '!' if x == 'META-INF/zigbert.rsa' else x) |
| 344 for name in names: | 322 for name in names: |
| 345 zip.writestr(name, files[name]) | 323 zip.writestr(name, files[name]) |
| 346 zip.close() | 324 zip.close() |
| 347 | 325 |
| 348 def createBuild(baseDir, outFile=None, locales=None, buildNum=None, releaseBuild
=False, keyFile=None, limitMetadata=False, multicompartment=False): | 326 def createBuild(baseDir, outFile=None, locales=None, buildNum=None, releaseBuild
=False, keyFile=None, limitMetadata=False, multicompartment=False): |
| 349 if buildNum == None: | |
| 350 buildNum = getBuildNum(baseDir) | |
| 351 if locales == None: | 327 if locales == None: |
| 352 locales = getLocales(baseDir) | 328 locales = getLocales(baseDir) |
| 353 elif locales == 'all': | 329 elif locales == 'all': |
| 354 locales = getLocales(baseDir, True) | 330 locales = getLocales(baseDir, True) |
| 355 | 331 |
| 356 metadata = readMetadata(baseDir) | 332 metadata = readMetadata(baseDir) |
| 357 version = metadata.get('general', 'version') | 333 version = getBuildVersion(baseDir, metadata, releaseBuild, buildNum) |
| 358 if not releaseBuild: | |
| 359 version += '.' + buildNum | |
| 360 | 334 |
| 361 if limitMetadata: | 335 if limitMetadata: |
| 362 for option in metadata.options('compat'): | 336 for option in metadata.options('compat'): |
| 363 if not option in ('firefox', 'thunderbird', 'seamonkey'): | 337 if not option in ('firefox', 'thunderbird', 'seamonkey'): |
| 364 metadata.remove_option('compat', option) | 338 metadata.remove_option('compat', option) |
| 365 | 339 |
| 366 if outFile == None: | 340 if outFile == None: |
| 367 outFile = getDefaultFileName(baseDir, metadata, version) | 341 outFile = getDefaultFileName(baseDir, metadata, version, 'xpi') |
| 368 | 342 |
| 369 contributors = getContributors(baseDir, metadata) | 343 contributors = getContributors(baseDir, metadata) |
| 370 | 344 |
| 371 params = { | 345 params = { |
| 372 'locales': locales, | 346 'locales': locales, |
| 373 'releaseBuild': releaseBuild, | 347 'releaseBuild': releaseBuild, |
| 374 'buildNum': buildNum, | |
| 375 'version': version.encode('utf-8'), | 348 'version': version.encode('utf-8'), |
| 376 'metadata': metadata, | 349 'metadata': metadata, |
| 377 'limitMetadata': limitMetadata, | 350 'limitMetadata': limitMetadata, |
| 378 'contributors': contributors, | 351 'contributors': contributors, |
| 379 'multicompartment': multicompartment, | 352 'multicompartment': multicompartment, |
| 380 } | 353 } |
| 381 files = {} | 354 files = {} |
| 382 files['install.rdf'] = createManifest(baseDir, params) | 355 files['install.rdf'] = createManifest(baseDir, params) |
| 383 for name, path in getChromeSubdirs(baseDir, params['locales']).iteritems(): | 356 for name, path in getChromeSubdirs(baseDir, params['locales']).iteritems(): |
| 384 if os.path.isdir(path): | 357 if os.path.isdir(path): |
| 385 readFile(files, params, path, 'chrome/%s' % name) | 358 readFile(files, params, path, 'chrome/%s' % name) |
| 386 if not params['limitMetadata']: | 359 if not params['limitMetadata']: |
| 387 fixupLocales(baseDir, files, params) | 360 fixupLocales(baseDir, files, params) |
| 388 readXPIFiles(baseDir, params, files) | 361 readXPIFiles(baseDir, params, files) |
| 389 if not 'bootstrap.js' in files: | 362 if not 'bootstrap.js' in files: |
| 390 addMissingFiles(baseDir, params, files) | 363 addMissingFiles(baseDir, params, files) |
| 391 if keyFile: | 364 if keyFile: |
| 392 signFiles(files, keyFile) | 365 signFiles(files, keyFile) |
| 393 writeXPI(files, outFile) | 366 writeXPI(files, outFile) |
| 394 | 367 |
| 395 def autoInstall(baseDir, host, port, multicompartment=False): | 368 def autoInstall(baseDir, host, port, multicompartment=False): |
| 396 fileBuffer = StringIO() | 369 fileBuffer = StringIO() |
| 397 createBuild(baseDir, outFile=fileBuffer, multicompartment=multicompartment) | 370 createBuild(baseDir, outFile=fileBuffer, multicompartment=multicompartment) |
| 398 urllib.urlopen('http://%s:%s/' % (host, port), data=fileBuffer.getvalue()) | 371 urllib.urlopen('http://%s:%s/' % (host, port), data=fileBuffer.getvalue()) |
| OLD | NEW |