| Index: packagerGecko.py |
| =================================================================== |
| --- a/packagerGecko.py |
| +++ b/packagerGecko.py |
| @@ -13,21 +13,20 @@ |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
| import os, sys, re, hashlib, base64, urllib, json |
| from ConfigParser import SafeConfigParser |
| from StringIO import StringIO |
| -from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED |
| import xml.dom.minidom as minidom |
| import buildtools.localeTools as localeTools |
| -from packager import getDefaultFileName, readMetadata, getBuildVersion, getTemplate |
| +from packager import getDefaultFileName, readMetadata, getBuildVersion, getTemplate, Files |
| KNOWN_APPS = { |
| 'conkeror': '{a79fe89b-6662-4ff4-8e88-09950ad4dfde}', |
| 'emusic': 'dlm@emusic.com', |
| 'fennec': '{a23983c0-fd0e-11dc-95ff-0800200c9a66}', |
| 'fennec2': '{aa3c5121-dab2-40e2-81ca-7ea25febc110}', |
| 'firefox': '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}', |
| 'midbrowser': '{aa5ca914-c309-495d-91cf-3141bbb04115}', |
| @@ -50,27 +49,29 @@ def getChromeSubdirs(baseDir, locales): |
| result = {} |
| chromeDir = getChromeDir(baseDir) |
| for subdir in ('content', 'skin'): |
| result[subdir] = os.path.join(chromeDir, subdir) |
| for locale in locales: |
| result['locale/%s' % locale] = os.path.join(chromeDir, 'locale', locale) |
| return result |
| -def getXPIFiles(baseDir): |
| - for file in ('components', 'modules', 'lib', 'resources', 'defaults', 'chrome.manifest', 'icon.png', 'icon64.png'): |
| - yield os.path.join(baseDir, file) |
| +def getPackageFiles(params): |
| + result = set(('chrome', 'components', 'modules', 'lib', 'resources', 'defaults', 'chrome.manifest', 'icon.png', 'icon64.png',)) |
| + |
| + baseDir = params['baseDir'] |
| for file in os.listdir(baseDir): |
| if file.endswith('.js') or file.endswith('.xml'): |
| - yield os.path.join(baseDir, file) |
| + result.add(file) |
| + return result |
| def getIgnoredFiles(params): |
| - result = ['.incomplete', 'meta.properties'] |
| + result = set(('.incomplete', 'meta.properties',)) |
| if params['releaseBuild']: |
| - result.append('timeline.js') |
| + result.add('timeline.js') |
| return result |
| def isValidLocale(localesDir, dir, includeIncomplete=False): |
| if re.search(r'[^\w\-]', dir): |
| return False |
| if not os.path.isdir(os.path.join(localesDir, dir)): |
| return False |
| if not includeIncomplete and os.path.exists(os.path.join(localesDir, dir, '.incomplete')): |
| @@ -80,25 +81,16 @@ def isValidLocale(localesDir, dir, inclu |
| def getLocales(baseDir, includeIncomplete=False): |
| global defaultLocale |
| localesDir = getLocalesDir(baseDir) |
| locales = filter(lambda dir: isValidLocale(localesDir, dir, includeIncomplete), os.listdir(localesDir)) |
| locales.sort(key=lambda x: '!' if x == defaultLocale else x) |
| return locales |
| def processFile(path, data, params): |
| - if not re.search(r'\.(manifest|xul|jsm?|xml|xhtml|rdf|dtd|properties|css)$', path): |
| - return data |
| - |
| - data = re.sub(r'\r', '', data) |
| - data = data.replace('{{VERSION}}', params['version']) |
| - |
| - whitespaceRegExp = re.compile(r'^( )+', re.M) |
| - data = re.sub(whitespaceRegExp, lambda match: '\t' * (len(match.group(0)) / 2), data) |
| - |
| if path.endswith('.manifest') and data.find('{{LOCALE}}') >= 0: |
| localesRegExp = re.compile(r'^(.*?){{LOCALE}}(.*?){{LOCALE}}(.*)$', re.M) |
| replacement = '\n'.join(map(lambda locale: r'\1%s\2%s\3' % (locale, locale), params['locales'])) |
| data = re.sub(localesRegExp, replacement, data) |
| if params['releaseBuild'] and path.endswith('.js'): |
| # Remove timeline calls from release builds |
| timelineRegExp1 = re.compile(r'^.*\b[tT]imeLine\.(\w+)\(.*', re.M) |
| @@ -151,44 +143,31 @@ def getContributors(baseDir, metadata): |
| def initTranslators(localeMetadata): |
| for locale in localeMetadata.itervalues(): |
| if 'translator' in locale: |
| locale['translators'] = sorted(map(lambda t: t.strip(), locale['translator'].split(',')), key=unicode.lower) |
| else: |
| locale['translators'] = [] |
| -def createManifest(baseDir, params): |
| +def createManifest(params): |
| global KNOWN_APPS, defaultLocale |
| template = getTemplate('install.rdf.tmpl', autoEscape=True) |
| templateData = dict(params) |
| - templateData['localeMetadata'] = readLocaleMetadata(baseDir, params['locales']) |
| + templateData['localeMetadata'] = readLocaleMetadata(params['baseDir'], params['locales']) |
| initTranslators(templateData['localeMetadata']) |
| templateData['KNOWN_APPS'] = KNOWN_APPS |
| templateData['defaultLocale'] = defaultLocale |
| return template.render(templateData).encode('utf-8') |
| -def readFile(files, params, path, name): |
| - ignoredFiles = getIgnoredFiles(params) |
| - if os.path.isdir(path): |
| - for file in os.listdir(path): |
| - if file in ignoredFiles: |
| - continue |
| - readFile(files, params, os.path.join(path, file), '%s/%s' % (name, file)) |
| - else: |
| - file = open(path, 'rb') |
| - data = processFile(path, file.read(), params) |
| - file.close() |
| - files[name] = data |
| - |
| -def fixupLocales(baseDir, files, params): |
| +def fixupLocales(params, files): |
| global defaultLocale |
| # Read in default locale data, it might not be included in files |
| - defaultLocaleDir = os.path.join(getLocalesDir(baseDir), defaultLocale) |
| + defaultLocaleDir = os.path.join(getLocalesDir(params['baseDir']), defaultLocale) |
| reference = {} |
| ignoredFiles = getIgnoredFiles(params) |
| for file in os.listdir(defaultLocaleDir): |
| path = os.path.join(defaultLocaleDir, file) |
| if file in ignoredFiles or not os.path.isfile(path): |
| continue |
| data = localeTools.readFile(path) |
| if data: |
| @@ -200,22 +179,17 @@ def fixupLocales(baseDir, files, params) |
| if path in files: |
| data = localeTools.parseString(files[path].decode('utf-8'), path) |
| for key, value in reference[file].iteritems(): |
| if not key in data: |
| files[path] += localeTools.generateStringEntry(key, value, path).encode('utf-8') |
| else: |
| files[path] = reference[file]['_origData'].encode('utf-8') |
| -def readXPIFiles(baseDir, params, files): |
| - for path in getXPIFiles(baseDir): |
| - if os.path.exists(path): |
| - readFile(files, params, path, os.path.basename(path)) |
| - |
| -def addMissingFiles(baseDir, params, files): |
| +def addMissingFiles(params, files): |
| templateData = { |
| 'hasChrome': False, |
| 'hasChromeRequires': False, |
| 'hasShutdownHandlers': False, |
| 'hasVersionPref': False, |
| 'chromeWindows': [], |
| 'requires': {}, |
| 'metadata': params['metadata'], |
| @@ -253,21 +227,21 @@ def addMissingFiles(baseDir, params, fil |
| if not moduleFile in files: |
| import buildtools |
| path = os.path.join(buildtools.__path__[0], moduleFile) |
| if os.path.exists(path): |
| missing.append((path, moduleFile)) |
| if not len(missing): |
| break |
| for path, moduleFile in missing: |
| - readFile(files, params, path, moduleFile) |
| + files.read(path, moduleFile) |
| checkScript(moduleFile) |
| template = getTemplate('bootstrap.js.tmpl') |
| - files['bootstrap.js'] = processFile('bootstrap.js', template.render(templateData).encode('utf-8'), params) |
| + files['bootstrap.js'] = template.render(templateData).encode('utf-8') |
| def signFiles(files, keyFile): |
| import M2Crypto |
| manifest = [] |
| signature = [] |
| def getDigest(data): |
| md5 = hashlib.md5() |
| @@ -308,55 +282,50 @@ def signFiles(files, keyFile): |
| mime.load_key(keyFile) |
| mime.set_x509_stack(stack) |
| signature = mime.sign(M2Crypto.BIO.MemoryBuffer(files['META-INF/zigbert.sf'].encode('utf-8')), M2Crypto.SMIME.PKCS7_DETACHED | M2Crypto.SMIME.PKCS7_BINARY) |
| buffer = M2Crypto.BIO.MemoryBuffer() |
| signature.write_der(buffer) |
| files['META-INF/zigbert.rsa'] = buffer.read() |
| -def writeXPI(files, outFile): |
| - zip = ZipFile(outFile, 'w', ZIP_DEFLATED) |
| - names = files.keys() |
| - names.sort(key=lambda x: '!' if x == 'META-INF/zigbert.rsa' else x) |
| - for name in names: |
| - zip.writestr(name, files[name]) |
| - zip.close() |
| - |
| def createBuild(baseDir, outFile=None, locales=None, buildNum=None, releaseBuild=False, keyFile=None, multicompartment=False): |
| if locales == None: |
| locales = getLocales(baseDir) |
| elif locales == 'all': |
| locales = getLocales(baseDir, True) |
| metadata = readMetadata(baseDir) |
| version = getBuildVersion(baseDir, metadata, releaseBuild, buildNum) |
| if outFile == None: |
| outFile = getDefaultFileName(baseDir, metadata, version, 'xpi') |
| contributors = getContributors(baseDir, metadata) |
| params = { |
| + 'baseDir': baseDir, |
| 'locales': locales, |
| 'releaseBuild': releaseBuild, |
| 'version': version.encode('utf-8'), |
| 'metadata': metadata, |
| 'contributors': contributors, |
| 'multicompartment': multicompartment, |
| } |
| - files = {} |
| - files['install.rdf'] = createManifest(baseDir, params) |
| + |
| + files = Files(getPackageFiles(params), getIgnoredFiles(params), |
| + process=lambda path, data: processFile(path, data, params)) |
| + files['install.rdf'] = createManifest(params) |
| + files.read(baseDir, skip=('chrome')) |
| for name, path in getChromeSubdirs(baseDir, params['locales']).iteritems(): |
| if os.path.isdir(path): |
| - readFile(files, params, path, 'chrome/%s' % name) |
| - fixupLocales(baseDir, files, params) |
| - readXPIFiles(baseDir, params, files) |
| + files.read(path, 'chrome/%s' % name) |
| + fixupLocales(params, files) |
| if not 'bootstrap.js' in files: |
| - addMissingFiles(baseDir, params, files) |
| + addMissingFiles(params, files) |
| if keyFile: |
| signFiles(files, keyFile) |
| - writeXPI(files, outFile) |
| + files.zip(outFile, sortKey=lambda x: '!' if x == 'META-INF/zigbert.rsa' else x) |
| def autoInstall(baseDir, host, port, multicompartment=False): |
| fileBuffer = StringIO() |
| createBuild(baseDir, outFile=fileBuffer, multicompartment=multicompartment) |
| urllib.urlopen('http://%s:%s/' % (host, port), data=fileBuffer.getvalue()) |