Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: packagerChrome.py

Issue 11544056: Prepared buildtools for Safari (Closed)
Left Patch Set: Prepared buildtools for Safari Created Sept. 6, 2013, 2:45 p.m.
Right Patch Set: Created Oct. 31, 2013, 3:40 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « packager.py ('k') | packagerGecko.py » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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-2013 Eyeo GmbH 4 # Copyright (C) 2006-2013 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 sys, os, re, json, struct 18 import sys, os, re, json, struct
19 from StringIO import StringIO 19 from StringIO import StringIO
20 20
21 import PIL.Image
22 import PIL.ImageMath
23
24 import packager 21 import packager
25 from packager import readMetadata, getMetadataPath, getDefaultFileName, getBuild Version, getTemplate, Files 22 from packager import readMetadata, getMetadataPath, getDefaultFileName, getBuild Version, getTemplate, Files
26 23
27 defaultLocale = 'en_US' 24 defaultLocale = 'en_US'
28 25
29 def getIgnoredFiles(params): 26 def getIgnoredFiles(params):
30 result = set(('store.description',)) 27 result = set(('store.description',))
31 28
32 # Hack: ignore all lib subdirectories 29 # Hack: ignore all lib subdirectories
33 libDir = os.path.join(params['baseDir'], 'lib') 30 libDir = os.path.join(params['baseDir'], 'lib')
34 for file in os.listdir(libDir): 31 for file in os.listdir(libDir):
35 if os.path.isdir(os.path.join(libDir, file)): 32 if os.path.isdir(os.path.join(libDir, file)):
36 result.add(file) 33 result.add(file)
37 return result 34 return result
38 35
39 def getPackageFiles(params): 36 def getPackageFiles(params):
40 result = set(('_locales', 'icons', 'jquery-ui', 'lib', 'skin', 'ui',)) 37 result = set(('_locales', 'icons', 'jquery-ui', 'lib', 'skin', 'ui', 'ext'))
41 38
42 if params['devenv']: 39 if params['devenv']:
43 result.add('qunit') 40 result.add('qunit')
44 41
45 baseDir = params['baseDir'] 42 baseDir = params['baseDir']
46 for file in os.listdir(baseDir): 43 for file in os.listdir(baseDir):
47 if file.endswith('.js') or file.endswith('.html') or file.endswith('.xml'): 44 if file.endswith('.js') or file.endswith('.html') or file.endswith('.xml'):
48 result.add(file) 45 result.add(file)
49 return result 46 return result
50 47
(...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after
275 'pt': 'pt_PT', 272 'pt': 'pt_PT',
276 } 273 }
277 for chromeLocale, operaLocale in operaMapping.iteritems(): 274 for chromeLocale, operaLocale in operaMapping.iteritems():
278 chromeFile = '_locales/%s/messages.json' % chromeLocale 275 chromeFile = '_locales/%s/messages.json' % chromeLocale
279 operaFile = '_locales/%s/messages.json' % operaLocale if operaLocale != No ne else None 276 operaFile = '_locales/%s/messages.json' % operaLocale if operaLocale != No ne else None
280 if chromeFile in files: 277 if chromeFile in files:
281 if operaFile != None: 278 if operaFile != None:
282 files[operaFile] = files[chromeFile] 279 files[operaFile] = files[chromeFile]
283 del files[chromeFile] 280 del files[chromeFile]
284 281
285 if params['type'] in ('opera', 'safari'):
286 # Hack: Replace "Chrome" by "Opera" or "Safari" in the locales
287 for path, data in files.iteritems():
288 if path.startswith("_locales/") and path.endswith("/messages.json"):
289 files[path] = re.sub(r"\bChrome\b", params['type'].capitalize(), data)
290
291 def signBinary(zipdata, keyFile): 282 def signBinary(zipdata, keyFile):
292 import M2Crypto 283 import M2Crypto
293 if not os.path.exists(keyFile): 284 if not os.path.exists(keyFile):
294 M2Crypto.RSA.gen_key(1024, 65537, callback=lambda x: None).save_key(keyFile, cipher=None) 285 M2Crypto.RSA.gen_key(1024, 65537, callback=lambda x: None).save_key(keyFile, cipher=None)
295 key = M2Crypto.EVP.load_key(keyFile) 286 key = M2Crypto.EVP.load_key(keyFile)
296 key.sign_init() 287 key.sign_init()
297 key.sign_update(zipdata) 288 key.sign_update(zipdata)
298 return key.final() 289 return key.final()
299 290
300 def getPublicKey(keyFile): 291 def getPublicKey(keyFile):
301 import M2Crypto 292 import M2Crypto
302 return M2Crypto.EVP.load_key(keyFile).as_der() 293 return M2Crypto.EVP.load_key(keyFile).as_der()
303 294
304 def writePackage(outputFile, pubkey, signature, zipdata): 295 def writePackage(outputFile, pubkey, signature, zipdata):
305 if isinstance(outputFile, basestring): 296 if isinstance(outputFile, basestring):
306 file = open(outputFile, 'wb') 297 file = open(outputFile, 'wb')
307 else: 298 else:
308 file = outputFile 299 file = outputFile
309 if pubkey != None and signature != None: 300 if pubkey != None and signature != None:
310 file.write(struct.pack('<4sIII', 'Cr24', 2, len(pubkey), len(signature))) 301 file.write(struct.pack('<4sIII', 'Cr24', 2, len(pubkey), len(signature)))
311 file.write(pubkey) 302 file.write(pubkey)
312 file.write(signature) 303 file.write(signature)
313 file.write(zipdata) 304 file.write(zipdata)
314 305
315 class ImageConverter(object):
316 def convert(self, params, files):
317 for filename, chain in params['metadata'].items('convert_img'):
318 steps = re.split(r'\s*->\s*', chain)
319 image = PIL.Image.open(steps.pop(0))
320
321 for step in steps:
322 filter, args = re.match(r'([^(]+)(?:\((.*)\))?', step).groups()
323 args = tuple(re.split(r'\s*,\s*', args)) if args else ()
324 image = getattr(self, 'filter_' + filter)(image, *args)
325
326 f = StringIO()
327 f.name = filename
328 image.save(f)
329 files[filename] = f.getvalue()
330
331 def filter_blend(self, image, *args):
332 if len(args) == 2: # args = (filename, opacity)
333 overlay = PIL.Image.open(args[0])
334
335 if image.mode != overlay.mode or image.mode == 'P':
336 overlay = overlay.convert('RGBA')
337 image = image.convert('RGBA')
338 elif len(args) == 4: # args = (red, green, blue, opacity)
339 overlay = PIL.Image.new('RGB', image.size, tuple(map(int, args[:3])))
340
341 if image.mode == 'P':
342 image = image.convert('RGBA')
343
344 if image.mode in ('RGBA', 'LA'):
345 overlay = PIL.Image.merge('RGBA', overlay.split() + image.split()[-1:])
346
347 if image.mode != overlay.mode:
348 image = image.convert(overlay.mode)
349 else:
350 raise TypeError
351
352 return PIL.Image.blend(image, overlay, float(args[-1]))
353
354 def filter_grayscale(self, image):
355 if image.mode == 'P':
356 image = image.convert('RGBA')
357
358 bands = list(image.split())
359 alpha = bands.pop(-1) if image.mode in ('RGBA', 'LA') else None
360
361 if len(bands) == 1:
362 return image
363
364 new_image = PIL.ImageMath.eval(
365 "convert((%s) / %d, 'L')" % (
366 ' + '.join('image%d' % i for i in xrange(len(bands))),
367 len(bands)
368 ),
369 **dict(('image%d' % i, band) for i, band in enumerate(bands))
370 )
371
372 if alpha:
373 new_image = PIL.Image.merge('LA', [new_image, alpha])
374
375 return new_image
376
377 def filter_colorToAlpha(self, image, *color):
378 bands = [band.convert('F') for band in image.convert('RGBA').split()]
379 color = map(float, color)
380
381 # Find the maximum difference rate between source and color. I had to use tw o
382 # difference functions because ImageMath.eval only evaluates the expression
383 # once.
384 alpha = PIL.ImageMath.eval('''\
385 float(
386 max(
387 max(
388 max(
389 difference1(red_band, cred_band),
390 difference1(green_band, cgreen_band)
391 ),
392 difference1(blue_band, cblue_band)
393 ),
394 max(
395 max(
396 difference2(red_band, cred_band),
397 difference2(green_band, cgreen_band)
398 ),
399 difference2(blue_band, cblue_band)
400 )
401 )
402 )''',
403 difference1=lambda source, col: (source - col) / (255.0 - col),
404 difference2=lambda source, col: (col - source) / col,
405
406 red_band = bands[0],
407 green_band= bands[1],
408 blue_band = bands[2],
409
410 cred_band = color[0],
411 cgreen_band= color[1],
412 cblue_band = color[2],
413 )
414
415 # Calculate the new image colors after the removal of the selected color
416 new_bands = [
417 PIL.ImageMath.eval(
418 "convert((image - color) / alpha + color, 'L')",
419 image=band,
420 color=col,
421 alpha=alpha
422 )
423 for band, col in zip(bands, color)
424 ]
425
426 # Add the new alpha band
427 new_bands.append(PIL.ImageMath.eval(
428 "convert(alpha_band * alpha, 'L')",
429 alpha = alpha,
430 alpha_band = bands[3]
431 ))
432
433 return PIL.Image.merge('RGBA', new_bands)
434
435 def createBuild(baseDir, type='chrome', outFile=None, buildNum=None, releaseBuil d=False, keyFile=None, experimentalAPI=False, devenv=False): 306 def createBuild(baseDir, type='chrome', outFile=None, buildNum=None, releaseBuil d=False, keyFile=None, experimentalAPI=False, devenv=False):
436 metadata = readMetadata(baseDir, type) 307 metadata = readMetadata(baseDir, type)
437 version = getBuildVersion(baseDir, metadata, releaseBuild, buildNum) 308 version = getBuildVersion(baseDir, metadata, releaseBuild, buildNum)
438 309
439 if outFile == None: 310 if outFile == None:
440 outFile = getDefaultFileName(baseDir, metadata, version, 'crx' if keyFile el se 'zip') 311 outFile = getDefaultFileName(baseDir, metadata, version, 'crx' if keyFile el se 'zip')
441 312
442 params = { 313 params = {
443 'type': type, 314 'type': type,
444 'baseDir': baseDir, 315 'baseDir': baseDir,
445 'releaseBuild': releaseBuild, 316 'releaseBuild': releaseBuild,
446 'version': version, 317 'version': version,
447 'experimentalAPI': experimentalAPI, 318 'experimentalAPI': experimentalAPI,
448 'devenv': devenv, 319 'devenv': devenv,
449 'metadata': metadata, 320 'metadata': metadata,
450 } 321 }
451 322
452 files = Files(getPackageFiles(params), getIgnoredFiles(params), 323 files = Files(getPackageFiles(params), getIgnoredFiles(params),
453 process=lambda path, data: processFile(path, data, params)) 324 process=lambda path, data: processFile(path, data, params))
454 files['manifest.json'] = createManifest(params) 325 files['manifest.json'] = createManifest(params)
455 if metadata.has_section('mapping'): 326 if metadata.has_section('mapping'):
456 files.readMappedFiles(metadata.items('mapping')) 327 files.readMappedFiles(metadata.items('mapping'))
457 files.read(baseDir) 328 files.read(baseDir)
458 329
459 if metadata.has_section('convert_js'): 330 if metadata.has_section('convert_js'):
460 convertJS(params, files) 331 convertJS(params, files)
461 332
462 if metadata.has_section('convert_img'): 333 if metadata.has_section('convert_img'):
463 ImageConverter().convert(params, files) 334 from imageConversion import convertImages
335 convertImages(params, files)
336
337 if metadata.has_section('preprocess'):
338 files.preprocess(
339 [f for f, _ in metadata.items('preprocess')],
340 {'needsExt': True}
341 )
464 342
465 if metadata.has_section('import_locales'): 343 if metadata.has_section('import_locales'):
466 importGeckoLocales(params, files) 344 importGeckoLocales(params, files)
467 345
468 if devenv: 346 if devenv:
469 files['devenvPoller__.js'] = createPoller(params) 347 files['devenvPoller__.js'] = createPoller(params)
470 348
471 if (metadata.has_option('general', 'backgroundScripts') and 349 if (metadata.has_option('general', 'backgroundScripts') and
472 'lib/info.js' in re.split(r'\s+', metadata.get('general', 'backgroundScrip ts')) and 350 'lib/info.js' in re.split(r'\s+', metadata.get('general', 'backgroundScrip ts')) and
473 'lib/info.js' not in files): 351 'lib/info.js' not in files):
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
506 def shutdown_server(server): 384 def shutdown_server(server):
507 time.sleep(10) 385 time.sleep(10)
508 server.shutdown() 386 server.shutdown()
509 thread.start_new_thread(shutdown_server, (server,)) 387 thread.start_new_thread(shutdown_server, (server,))
510 server.serve_forever() 388 server.serve_forever()
511 389
512 if connections[0] == 0: 390 if connections[0] == 0:
513 print 'Warning: No incoming connections, extension probably not active in th e browser yet' 391 print 'Warning: No incoming connections, extension probably not active in th e browser yet'
514 else: 392 else:
515 print 'Handled %i connection(s)' % connections[0] 393 print 'Handled %i connection(s)' % connections[0]
LEFTRIGHT

Powered by Google App Engine
This is Rietveld