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

Delta Between Two Patch Sets: packagerChrome.py

Issue 29549786: Issue 5535 - Replace our module system with webpack (Closed)
Left Patch Set: Addressed Wladimir's feedback, use JSON and standard in + out Created Oct. 9, 2017, 1:50 p.m.
Right Patch Set: Addressed final nits Created Oct. 10, 2017, 5:02 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 | « package-lock.json ('k') | packagerEdge.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 # This Source Code Form is subject to the terms of the Mozilla Public 1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this 2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 4
5 import errno 5 import errno
6 import glob
6 import io 7 import io
7 import json 8 import json
8 import os 9 import os
9 import re 10 import re
10 from StringIO import StringIO 11 from StringIO import StringIO
11 import struct 12 import struct
12 import subprocess 13 import subprocess
13 import sys 14 import sys
14 15
15 from ensure_dependencies import read_deps
16 from packager import (readMetadata, getDefaultFileName, getBuildVersion, 16 from packager import (readMetadata, getDefaultFileName, getBuildVersion,
17 getTemplate, Files) 17 getTemplate, Files)
18 18
19 defaultLocale = 'en_US' 19 defaultLocale = 'en_US'
20 20
21 21
22 def getIgnoredFiles(params): 22 def getIgnoredFiles(params):
23 return {'store.description'} 23 return {'store.description'}
24 24
25 25
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after
181 181
182 bundle_file = os.path.relpath(os.path.join(base_item_path, name), 182 bundle_file = os.path.relpath(os.path.join(base_item_path, name),
183 base_extension_path) 183 base_extension_path)
184 entry_files = [os.path.join(base_item_path, module_path) 184 entry_files = [os.path.join(base_item_path, module_path)
185 for module_path in value.split()] 185 for module_path in value.split()]
186 configuration['bundles'].append({ 186 configuration['bundles'].append({
187 'bundle_name': bundle_file, 187 'bundle_name': bundle_file,
188 'entry_points': entry_files, 188 'entry_points': entry_files,
189 }) 189 })
190 190
191 command = ['node', 191 cmd = ['node', os.path.join(os.path.dirname(__file__), 'webpack_runner.js')]
192 os.path.join(os.path.dirname(__file__), 'webpack_runner.js')] 192 process = subprocess.Popen(cmd, stdout=subprocess.PIPE,
193 process = subprocess.Popen( 193 stdin=subprocess.PIPE)
194 command, stdout=subprocess.PIPE, stdin=subprocess.PIPE
195 )
196 output = process.communicate(input=toJson(configuration))[0] 194 output = process.communicate(input=toJson(configuration))[0]
197 if process.returncode != 0: 195 if process.returncode != 0:
198 raise subprocess.CalledProcessError(process.returncode, cmd=command) 196 raise subprocess.CalledProcessError(process.returncode, cmd=cmd)
199 197
200 bundles = json.loads(output) 198 bundles = json.loads(output)
201 for bundle in bundles: 199 for bundle in bundles:
202 files[bundle] = bundles[bundle].encode('utf-8') 200 files[bundle] = bundles[bundle].encode('utf-8')
203 201
204 202
205 def import_string_webext(data, key, source):
206 """Import a single translation from the source dictionary into data"""
207 data[key] = source
208
209
210 def import_string_gecko(data, key, value):
211 """Import Gecko-style locales into data.
212
213 Only sets {'message': value} in the data-dictionary, after stripping
214 undesired Gecko-style access keys.
215 """
216 match = re.search(r'^(.*?)\s*\(&.\)$', value)
217 if match:
218 value = match.group(1)
219 else:
220 index = value.find('&')
221 if index >= 0:
222 value = value[0:index] + value[index + 1:]
223
224 data[key] = {'message': value}
225
226
227 def import_locales(params, files): 203 def import_locales(params, files):
228 import localeTools 204 for item in params['metadata'].items('import_locales'):
229 205 filename, keys = item
230 # FIXME: localeTools doesn't use real Chrome locales, it uses dash as 206 for sourceFile in glob.glob(os.path.join(os.path.dirname(item.source),
231 # separator instead. 207 *filename.split('/'))):
232 convert_locale_code = lambda code: code.replace('-', '_') 208 locale = sourceFile.split(os.path.sep)[-2]
233 209 targetFile = os.path.join('_locales', locale, 'messages.json')
234 # We need to map Chrome locales to Gecko locales. Start by mapping Chrome 210 data = json.loads(files.get(targetFile, '{}').decode('utf-8'))
235 # locales to themselves, merely with the dash as separator.
236 locale_mapping = {convert_locale_code(l): l for l in localeTools.chromeLocal es}
237
238 # Convert values to Crowdin locales first (use Chrome => Crowdin mapping).
239 for chrome_locale, crowdin_locale in localeTools.langMappingChrome.iteritems ():
240 locale_mapping[convert_locale_code(chrome_locale)] = crowdin_locale
241
242 # Now convert values to Gecko locales (use Gecko => Crowdin mapping).
243 reverse_mapping = {v: k for k, v in locale_mapping.iteritems()}
244 for gecko_locale, crowdin_locale in localeTools.langMappingGecko.iteritems() :
245 if crowdin_locale in reverse_mapping:
246 locale_mapping[reverse_mapping[crowdin_locale]] = gecko_locale
247
248 for target, source in locale_mapping.iteritems():
249 targetFile = '_locales/%s/messages.json' % target
250 if not targetFile in files:
251 continue
252
253 for item in params['metadata'].items('import_locales'):
254 fileName, keys = item
255 parts = map(lambda n: source if n == '*' else n, fileName.split('/') )
256 sourceFile = os.path.join(os.path.dirname(item.source), *parts)
257 incompleteMarker = os.path.join(os.path.dirname(sourceFile), '.incom plete')
258 if not os.path.exists(sourceFile) or os.path.exists(incompleteMarker ):
259 continue
260
261 data = json.loads(files[targetFile].decode('utf-8'))
262 211
263 try: 212 try:
264 # The WebExtensions (.json) and Gecko format provide 213 with io.open(sourceFile, 'r', encoding='utf-8') as handle:
265 # translations differently and/or provide additional 214 sourceData = json.load(handle)
266 # information like e.g. "placeholders". We want to adhere to
267 # that and preserve the addtional info.
268 if sourceFile.endswith('.json'):
269 with io.open(sourceFile, 'r', encoding='utf-8') as handle:
270 sourceData = json.load(handle)
271 import_string = import_string_webext
272 else:
273 sourceData = localeTools.readFile(sourceFile)
274 import_string = import_string_gecko
275 215
276 # Resolve wildcard imports 216 # Resolve wildcard imports
277 if keys == '*' or keys == '=*': 217 if keys == '*':
278 importList = sourceData.keys() 218 importList = sourceData.keys()
279 importList = filter(lambda k: not k.startswith('_'), importL ist) 219 importList = filter(lambda k: not k.startswith('_'), importL ist)
280 if keys == '=*':
281 importList = map(lambda k: '=' + k, importList)
282 keys = ' '.join(importList) 220 keys = ' '.join(importList)
283 221
284 for stringID in keys.split(): 222 for stringID in keys.split():
285 noMangling = False
286 if stringID.startswith('='):
287 stringID = stringID[1:]
288 noMangling = True
289
290 if stringID in sourceData: 223 if stringID in sourceData:
291 if noMangling: 224 if stringID in data:
292 key = re.sub(r'\W', '_', stringID) 225 print ('Warning: locale string {} defined multiple'
293 else: 226 ' times').format(stringID)
294 key = re.sub(r'\..*', '', parts[-1]) + '_' + re.sub( r'\W', '_', stringID) 227
295 if key in data: 228 data[stringID] = sourceData[stringID]
296 print 'Warning: locale string %s defined multiple ti mes' % key
297
298 import_string(data, key, sourceData[stringID])
299 except Exception as e: 229 except Exception as e:
300 print 'Warning: error importing locale data from %s: %s' % (sour ceFile, e) 230 print 'Warning: error importing locale data from %s: %s' % (sour ceFile, e)
301 231
302 files[targetFile] = toJson(data) 232 files[targetFile] = toJson(data)
303 233
304 234
305 def truncate(text, length_limit): 235 def truncate(text, length_limit):
306 if len(text) <= length_limit: 236 if len(text) <= length_limit:
307 return text 237 return text
308 return text[:length_limit - 1].rstrip() + u'\u2026' 238 return text[:length_limit - 1].rstrip() + u'\u2026'
309 239
310 240
311 def fixTranslationsForCWS(files): 241 def fix_translations_for_chrome(files):
312 # Chrome Web Store requires messages used in manifest.json to be present in
313 # all languages. It also enforces length limits for extension names and
314 # descriptions.
315 defaults = {} 242 defaults = {}
316 data = json.loads(files['_locales/%s/messages.json' % defaultLocale]) 243 data = json.loads(files['_locales/%s/messages.json' % defaultLocale])
317 for match in re.finditer(r'__MSG_(\S+)__', files['manifest.json']): 244 for match in re.finditer(r'__MSG_(\S+)__', files['manifest.json']):
318 name = match.group(1) 245 name = match.group(1)
319 defaults[name] = data[name] 246 defaults[name] = data[name]
320 247
321 limits = {} 248 limits = {}
322 manifest = json.loads(files['manifest.json']) 249 manifest = json.loads(files['manifest.json'])
323 for key, limit in (('name', 45), ('description', 132), ('short_name', 12)): 250 for key, limit in (('name', 45), ('description', 132), ('short_name', 12)):
324 match = re.search(r'__MSG_(\S+)__', manifest.get(key, '')) 251 match = re.search(r'__MSG_(\S+)__', manifest.get(key, ''))
325 if match: 252 if match:
326 limits[match.group(1)] = limit 253 limits[match.group(1)] = limit
327 254
328 for filename in files: 255 for path in list(files):
329 if not filename.startswith('_locales/') or not filename.endswith('/messa ges.json'): 256 match = re.search(r'^_locales/(?:es_(AR|CL|(MX))|[^/]+)/(.*)', path)
257 if not match:
330 continue 258 continue
331 259
332 data = json.loads(files[filename]) 260 # The Chrome Web Store requires messages used in manifest.json to
333 for name, info in defaults.iteritems(): 261 # be present in all languages, and enforces length limits on
334 data.setdefault(name, info) 262 # extension name and description.
335 for name, limit in limits.iteritems(): 263 is_latam, is_mexican, filename = match.groups()
336 if name in data: 264 if filename == 'messages.json':
337 data[name]['message'] = truncate(data[name]['message'], limit) 265 data = json.loads(files[path])
338 files[filename] = toJson(data) 266 for name, info in defaults.iteritems():
267 data.setdefault(name, info)
268 for name, limit in limits.iteritems():
269 info = data.get(name)
270 if info:
271 info['message'] = truncate(info['message'], limit)
272 files[path] = toJson(data)
273
274 # Chrome combines Latin American dialects of Spanish into es-419.
275 if is_latam:
276 data = files.pop(path)
277 if is_mexican:
278 files['_locales/es_419/' + filename] = data
339 279
340 280
341 def signBinary(zipdata, keyFile): 281 def signBinary(zipdata, keyFile):
342 from Crypto.Hash import SHA 282 from Crypto.Hash import SHA
343 from Crypto.PublicKey import RSA 283 from Crypto.PublicKey import RSA
344 from Crypto.Signature import PKCS1_v1_5 284 from Crypto.Signature import PKCS1_v1_5
345 285
346 try: 286 try:
347 with open(keyFile, 'rb') as file: 287 with open(keyFile, 'rb') as file:
348 key = RSA.importKey(file.read()) 288 key = RSA.importKey(file.read())
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
408 files.preprocess( 348 files.preprocess(
409 [f for f, _ in metadata.items('preprocess')], 349 [f for f, _ in metadata.items('preprocess')],
410 {'needsExt': True} 350 {'needsExt': True}
411 ) 351 )
412 352
413 if metadata.has_section('import_locales'): 353 if metadata.has_section('import_locales'):
414 import_locales(params, files) 354 import_locales(params, files)
415 355
416 files['manifest.json'] = createManifest(params, files) 356 files['manifest.json'] = createManifest(params, files)
417 if type == 'chrome': 357 if type == 'chrome':
418 fixTranslationsForCWS(files) 358 fix_translations_for_chrome(files)
419 359
420 if devenv: 360 if devenv:
421 import buildtools 361 import buildtools
422 import random 362 import random
423 files.read(os.path.join(buildtools.__path__[0], 'chromeDevenvPoller__.js '), relpath='devenvPoller__.js') 363 files.read(os.path.join(buildtools.__path__[0], 'chromeDevenvPoller__.js '), relpath='devenvPoller__.js')
424 files['devenvVersion__'] = str(random.random()) 364 files['devenvVersion__'] = str(random.random())
425 365
426 if metadata.has_option('general', 'testScripts'): 366 if metadata.has_option('general', 'testScripts'):
427 files['qunit/index.html'] = createScriptPage( 367 files['qunit/index.html'] = createScriptPage(
428 params, 'testIndex.html.tmpl', ('general', 'testScripts') 368 params, 'testIndex.html.tmpl', ('general', 'testScripts')
429 ) 369 )
430 370
431 zipdata = files.zipToString() 371 zipdata = files.zipToString()
432 signature = None 372 signature = None
433 pubkey = None 373 pubkey = None
434 if keyFile != None: 374 if keyFile != None:
435 signature = signBinary(zipdata, keyFile) 375 signature = signBinary(zipdata, keyFile)
436 pubkey = getPublicKey(keyFile) 376 pubkey = getPublicKey(keyFile)
437 writePackage(outFile, pubkey, signature, zipdata) 377 writePackage(outFile, pubkey, signature, zipdata)
LEFTRIGHT

Powered by Google App Engine
This is Rietveld