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

Side by Side Diff: packagerChrome.py

Issue 11544056: Prepared buildtools for Safari (Closed)
Patch Set: Created Sept. 9, 2013, 9:25 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
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
Wladimir Palant 2013/09/10 10:15:27 Please don't require these modules at the top leve
23
21 import packager 24 import packager
22 from packager import readMetadata, getMetadataPath, getDefaultFileName, getBuild Version, getTemplate, Files 25 from packager import readMetadata, getMetadataPath, getDefaultFileName, getBuild Version, getTemplate, Files
23 26
24 defaultLocale = 'en_US' 27 defaultLocale = 'en_US'
25 28
26 def getIgnoredFiles(params): 29 def getIgnoredFiles(params):
27 result = set(('store.description',)) 30 result = set(('store.description',))
28 31
29 # Hack: ignore all lib subdirectories 32 # Hack: ignore all lib subdirectories
30 libDir = os.path.join(params['baseDir'], 'lib') 33 libDir = os.path.join(params['baseDir'], 'lib')
(...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after
272 'pt': 'pt_PT', 275 'pt': 'pt_PT',
273 } 276 }
274 for chromeLocale, operaLocale in operaMapping.iteritems(): 277 for chromeLocale, operaLocale in operaMapping.iteritems():
275 chromeFile = '_locales/%s/messages.json' % chromeLocale 278 chromeFile = '_locales/%s/messages.json' % chromeLocale
276 operaFile = '_locales/%s/messages.json' % operaLocale if operaLocale != No ne else None 279 operaFile = '_locales/%s/messages.json' % operaLocale if operaLocale != No ne else None
277 if chromeFile in files: 280 if chromeFile in files:
278 if operaFile != None: 281 if operaFile != None:
279 files[operaFile] = files[chromeFile] 282 files[operaFile] = files[chromeFile]
280 del files[chromeFile] 283 del files[chromeFile]
281 284
282 # Hack: Replace "Chrome" by "Opera" in the locales 285 if params['type'] in ('opera', 'safari'):
286 # Hack: Replace "Chrome" by "Opera" or "Safari" in the locales
283 for path, data in files.iteritems(): 287 for path, data in files.iteritems():
284 if path.startswith("_locales/") and path.endswith("/messages.json"): 288 if path.startswith("_locales/") and path.endswith("/messages.json"):
285 files[path] = re.sub(r"\bChrome\b", "Opera", data) 289 files[path] = re.sub(r"\bChrome\b", params['type'].capitalize(), data)
286 290
287 def signBinary(zipdata, keyFile): 291 def signBinary(zipdata, keyFile):
288 import M2Crypto 292 import M2Crypto
289 if not os.path.exists(keyFile): 293 if not os.path.exists(keyFile):
290 M2Crypto.RSA.gen_key(1024, 65537, callback=lambda x: None).save_key(keyFile, cipher=None) 294 M2Crypto.RSA.gen_key(1024, 65537, callback=lambda x: None).save_key(keyFile, cipher=None)
291 key = M2Crypto.EVP.load_key(keyFile) 295 key = M2Crypto.EVP.load_key(keyFile)
292 key.sign_init() 296 key.sign_init()
293 key.sign_update(zipdata) 297 key.sign_update(zipdata)
294 return key.final() 298 return key.final()
295 299
296 def getPublicKey(keyFile): 300 def getPublicKey(keyFile):
297 import M2Crypto 301 import M2Crypto
298 return M2Crypto.EVP.load_key(keyFile).as_der() 302 return M2Crypto.EVP.load_key(keyFile).as_der()
299 303
300 def writePackage(outputFile, pubkey, signature, zipdata): 304 def writePackage(outputFile, pubkey, signature, zipdata):
301 if isinstance(outputFile, basestring): 305 if isinstance(outputFile, basestring):
302 file = open(outputFile, 'wb') 306 file = open(outputFile, 'wb')
303 else: 307 else:
304 file = outputFile 308 file = outputFile
305 if pubkey != None and signature != None: 309 if pubkey != None and signature != None:
306 file.write(struct.pack('<4sIII', 'Cr24', 2, len(pubkey), len(signature))) 310 file.write(struct.pack('<4sIII', 'Cr24', 2, len(pubkey), len(signature)))
307 file.write(pubkey) 311 file.write(pubkey)
308 file.write(signature) 312 file.write(signature)
309 file.write(zipdata) 313 file.write(zipdata)
310 314
315 class ImageConverter(object):
316 def convert(self, params, files):
Wladimir Palant 2013/09/10 10:15:27 Does this need to be an object? You are never real
Sebastian Noack 2013/09/10 12:40:43 I'm using self to access the filters. Yes, I could
Felix Dahlke 2013/09/11 14:44:35 I'd vote for having this as a bunch of free functi
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))
Wladimir Palant 2013/09/10 10:15:27 This path should be relative to the metadata file
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])
Wladimir Palant 2013/09/10 10:15:27 This file name should be resolved relative to the
334
335 if image.mode != overlay.mode or image.mode == 'P':
Wladimir Palant 2013/09/10 10:15:27 Please comment here that 'P' means "palette" (whic
336 overlay = overlay.convert('RGBA')
337 image = image.convert('RGBA')
Wladimir Palant 2013/09/10 10:15:27 Style nit: we don't usually align equal signs and
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
Wladimir Palant 2013/09/10 10:15:27 How about: raise TypeError("Wrong number of b
351
352 return PIL.Image.blend(image, overlay, float(args[-1]))
Wladimir Palant 2013/09/10 10:15:27 I suggest processing the opacity parameter when yo
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 )
Wladimir Palant 2013/09/10 10:15:27 Won't image.convert("LA", dither=None) do the righ
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):
Wladimir Palant 2013/09/10 10:15:27 I cannot really imagine what we would use this for
Sebastian Noack 2013/09/10 12:40:43 Safari ignores all image data except the alpha cha
378 bands = [band.convert('F') for band in image.convert('RGBA').split()]
379 color = map(float, color)
Wladimir Palant 2013/09/10 14:02:12 Shouldn't we verify that three colors are given an
Sebastian Noack 2013/09/10 16:50:05 I'm not a big fan of adding extra code to throw an
Felix Dahlke 2013/09/11 14:44:35 I agree somewhat. However, in this case, I think i
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,
Wladimir Palant 2013/09/10 14:02:12 I have a pretty hard time judging whether this alg
Sebastian Noack 2013/09/10 16:50:05 Yes you could do that in plain python instead of w
Wladimir Palant 2013/09/11 06:41:36 Given the complexity of this implementation I'm go
Felix Dahlke 2013/09/11 14:44:35 I prefer the approach suggested by Wladimir for th
Sebastian Noack 2013/09/11 15:48:06 I still don't see how not using PIL.ImageMath woul
Wladimir Palant 2013/09/11 16:14:56 As I said, converting to greyscale and taking any
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 )
Wladimir Palant 2013/09/10 14:02:12 Please indicate the source of your code whenever y
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
311 def createBuild(baseDir, type='chrome', outFile=None, buildNum=None, releaseBuil d=False, keyFile=None, experimentalAPI=False, devenv=False): 435 def createBuild(baseDir, type='chrome', outFile=None, buildNum=None, releaseBuil d=False, keyFile=None, experimentalAPI=False, devenv=False):
312 metadata = readMetadata(baseDir, type) 436 metadata = readMetadata(baseDir, type)
313 version = getBuildVersion(baseDir, metadata, releaseBuild, buildNum) 437 version = getBuildVersion(baseDir, metadata, releaseBuild, buildNum)
314 438
315 if outFile == None: 439 if outFile == None:
316 outFile = getDefaultFileName(baseDir, metadata, version, 'crx' if keyFile el se 'zip') 440 outFile = getDefaultFileName(baseDir, metadata, version, 'crx' if keyFile el se 'zip')
317 441
318 params = { 442 params = {
319 'type': type, 443 'type': type,
320 'baseDir': baseDir, 444 'baseDir': baseDir,
321 'releaseBuild': releaseBuild, 445 'releaseBuild': releaseBuild,
322 'version': version, 446 'version': version,
323 'experimentalAPI': experimentalAPI, 447 'experimentalAPI': experimentalAPI,
324 'devenv': devenv, 448 'devenv': devenv,
325 'metadata': metadata, 449 'metadata': metadata,
326 } 450 }
327 451
328 files = Files(getPackageFiles(params), getIgnoredFiles(params), 452 files = Files(getPackageFiles(params), getIgnoredFiles(params),
329 process=lambda path, data: processFile(path, data, params)) 453 process=lambda path, data: processFile(path, data, params))
330 files['manifest.json'] = createManifest(params) 454 files['manifest.json'] = createManifest(params)
331 if metadata.has_section('mapping'): 455 if metadata.has_section('mapping'):
332 files.readMappedFiles(metadata.items('mapping')) 456 files.readMappedFiles(metadata.items('mapping'))
333 files.read(baseDir) 457 files.read(baseDir)
334 458
335 if metadata.has_section('convert_js'): 459 if metadata.has_section('convert_js'):
336 convertJS(params, files) 460 convertJS(params, files)
337 461
462 if metadata.has_section('convert_img'):
463 ImageConverter().convert(params, files)
464
338 if metadata.has_section('import_locales'): 465 if metadata.has_section('import_locales'):
339 importGeckoLocales(params, files) 466 importGeckoLocales(params, files)
340 467
341 if devenv: 468 if devenv:
342 files['devenvPoller__.js'] = createPoller(params) 469 files['devenvPoller__.js'] = createPoller(params)
343 470
344 if (metadata.has_option('general', 'backgroundScripts') and 471 if (metadata.has_option('general', 'backgroundScripts') and
345 'lib/info.js' in re.split(r'\s+', metadata.get('general', 'backgroundScrip ts')) and 472 'lib/info.js' in re.split(r'\s+', metadata.get('general', 'backgroundScrip ts')) and
346 'lib/info.js' not in files): 473 'lib/info.js' not in files):
347 files['lib/info.js'] = createInfoModule(params) 474 files['lib/info.js'] = createInfoModule(params)
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
379 def shutdown_server(server): 506 def shutdown_server(server):
380 time.sleep(10) 507 time.sleep(10)
381 server.shutdown() 508 server.shutdown()
382 thread.start_new_thread(shutdown_server, (server,)) 509 thread.start_new_thread(shutdown_server, (server,))
383 server.serve_forever() 510 server.serve_forever()
384 511
385 if connections[0] == 0: 512 if connections[0] == 0:
386 print 'Warning: No incoming connections, extension probably not active in th e browser yet' 513 print 'Warning: No incoming connections, extension probably not active in th e browser yet'
387 else: 514 else:
388 print 'Handled %i connection(s)' % connections[0] 515 print 'Handled %i connection(s)' % connections[0]
OLDNEW

Powered by Google App Engine
This is Rietveld