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

Side by Side Diff: packagerEdge.py

Issue 29825555: Issue 6291 - add ManifoldJS packaging for Edge (Closed) Base URL: https://hg.adblockplus.org/buildtools/file/9a56d76cd951
Patch Set: Created July 9, 2018, 7:52 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 # 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 base64 5 import datetime
6 import hashlib
7 import json 6 import json
8 import mimetypes
9 import os 7 import os
10 import zipfile 8 import shutil
9 import subprocess
11 10
12 import packager 11 import packager
13 import packagerChrome 12 import packagerChrome
14 13
15 # Files and directories expected inside of the .APPX archive. 14 # Files and directories expected inside of the .APPX archive.
16 MANIFEST = 'AppxManifest.xml' 15 MANIFEST = 'appxmanifest.xml'
Sebastian Noack 2018/07/09 12:35:20 How come that this changed?
tlucas 2018/07/09 13:23:56 manifoldjs only accepts "AppxManifest.xml" to be t
17 CONTENT_TYPES = '[Content_Types].xml' 16 CONTENT_TYPES = '[Content_Types].xml'
18 BLOCKMAP = 'AppxBlockMap.xml' 17 BLOCKMAP = 'AppxBlockMap.xml'
19 EXTENSION_DIR = 'Extension' 18 EXTENSION_DIR = 'Extension'
20 ASSETS_DIR = 'Assets' 19 ASSETS_DIR = 'Assets'
21 20
22 # Size of uncompressed block in the APPX block map. 21 # Size of uncompressed block in the APPX block map.
23 BLOCKSIZE = 64 * 1024 22 BLOCKSIZE = 64 * 1024
24 23
25 defaultLocale = packagerChrome.defaultLocale 24 defaultLocale = packagerChrome.defaultLocale
26 25
27 26
28 def _get_template_for(filename): 27 def _get_template_for(filename):
29 return packager.getTemplate('edge/{}.tmpl'.format(filename)) 28 return packager.getTemplate('edge/{}.tmpl'.format(filename))
30 29
31 30
32 def _lfh_size(filename):
33 """Compute the size of zip local file header for `filename`."""
34 try:
35 filename = filename.encode('utf-8')
36 except UnicodeDecodeError:
37 pass # filename is already a byte string.
38 return zipfile.sizeFileHeader + len(filename)
39
40
41 def _make_blockmap_entry(filename, data):
42 blocks = [data[i:i + BLOCKSIZE] for i in range(0, len(data), BLOCKSIZE)]
43 return {
44 'name': filename.replace('/', '\\'),
45 'size': len(data),
46 'lfh_size': _lfh_size(filename),
47 'blocks': [
48 {'hash': base64.b64encode(hashlib.sha256(block).digest())}
49 for block in blocks
50 ],
51 }
52
53
54 def create_appx_blockmap(files):
55 """Create APPX blockmap for the list of files."""
56 # We don't support AppxBlockmap.xml generation for compressed zip files at
57 # the moment. The only way to reliably calculate the compressed size of
58 # each 64k chunk in the zip file is to override the relevant parts of
59 # `zipfile` library. We have chosen to not do it so we produce an
60 # uncompressed zip file that is later repackaged by Windows Store with
61 # compression.
62 template = _get_template_for(BLOCKMAP)
63 files = [_make_blockmap_entry(n, d) for n, d in files.items()]
64 return template.render(files=files).encode('utf-8')
65
66
67 def load_translation(files, locale): 31 def load_translation(files, locale):
68 """Load translation strings for locale from files.""" 32 """Load translation strings for locale from files."""
69 path = '{}/_locales/{}/messages.json'.format(EXTENSION_DIR, locale) 33 path = '{}/_locales/{}/messages.json'.format(EXTENSION_DIR, locale)
70 return json.loads(files[path]) 34 return json.loads(files[path])
71 35
72 36
73 def get_appx_version(metadata, build_num): 37 def get_appx_version(metadata, build_num):
74 """Get the version number for usage in AppxManifest.xml. 38 """Get the version number for usage in AppxManifest.xml.
75 39
76 As required by the Windows Store, the returned version string has four 40 As required by the Windows Store, the returned version string has four
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
119 def move_files_to_extension(files): 83 def move_files_to_extension(files):
120 """Move all files into `Extension` folder for APPX packaging.""" 84 """Move all files into `Extension` folder for APPX packaging."""
121 # We sort the files to ensure that 'Extension/xyz' is moved before 'xyz'. 85 # We sort the files to ensure that 'Extension/xyz' is moved before 'xyz'.
122 # If 'xyz' is moved first, it would overwrite 'Extension/xyz' and its 86 # If 'xyz' is moved first, it would overwrite 'Extension/xyz' and its
123 # original content would be lost. 87 # original content would be lost.
124 names = sorted(files.keys(), key=len, reverse=True) 88 names = sorted(files.keys(), key=len, reverse=True)
125 for filename in names: 89 for filename in names:
126 files['{}/{}'.format(EXTENSION_DIR, filename)] = files.pop(filename) 90 files['{}/{}'.format(EXTENSION_DIR, filename)] = files.pop(filename)
127 91
128 92
129 def create_content_types_map(filenames):
130 """Create [Content_Types].xml -- a mime type map."""
131 params = {'defaults': {}, 'overrides': {}}
132 overrides = {
133 BLOCKMAP: 'application/vnd.ms-appx.blockmap+xml',
134 MANIFEST: 'application/vnd.ms-appx.manifest+xml',
135 }
136 types = mimetypes.MimeTypes()
137 types.add_type('application/octet-stream', '.otf')
138 for filename in filenames:
139 ext = os.path.splitext(filename)[1]
140 if ext:
141 content_type = types.guess_type(filename, strict=False)[0]
142 if content_type is not None:
143 params['defaults'][ext[1:]] = content_type
144 if filename in overrides:
145 params['overrides']['/' + filename] = overrides[filename]
146 content_types_template = _get_template_for(CONTENT_TYPES)
147 return content_types_template.render(params).encode('utf-8')
148
149
150 def createBuild(baseDir, type='edge', outFile=None, # noqa: preserve API. 93 def createBuild(baseDir, type='edge', outFile=None, # noqa: preserve API.
151 buildNum=None, releaseBuild=False, keyFile=None, 94 buildNum=None, releaseBuild=False, keyFile=None,
152 devenv=False): 95 devenv=False):
153 96
154 metadata = packager.readMetadata(baseDir, type) 97 metadata = packager.readMetadata(baseDir, type)
155 version = packager.getBuildVersion(baseDir, metadata, releaseBuild, 98 version = packager.getBuildVersion(baseDir, metadata, releaseBuild,
156 buildNum) 99 buildNum)
157 100
158 outfile = outFile or packager.getDefaultFileName(metadata, version, 'appx') 101 outfile = outFile or packager.getDefaultFileName(metadata, version, 'appx')
159 102
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
193 136
194 move_files_to_extension(files) 137 move_files_to_extension(files)
195 138
196 if metadata.has_section('appx_assets'): 139 if metadata.has_section('appx_assets'):
197 for name, path in metadata.items('appx_assets'): 140 for name, path in metadata.items('appx_assets'):
198 path = os.path.join(baseDir, path) 141 path = os.path.join(baseDir, path)
199 files.read(path, '{}/{}'.format(ASSETS_DIR, name)) 142 files.read(path, '{}/{}'.format(ASSETS_DIR, name))
200 143
201 files[MANIFEST] = create_appx_manifest(params, files, 144 files[MANIFEST] = create_appx_manifest(params, files,
202 buildNum, releaseBuild) 145 buildNum, releaseBuild)
203 files[BLOCKMAP] = create_appx_blockmap(files) 146 tmp_dir = files.to_tmp_folder('edgeextension/manifest')
204 files[CONTENT_TYPES] = create_content_types_map(files.keys() + [BLOCKMAP]) 147 try:
Sebastian Noack 2018/07/09 12:35:20 Any reason you didn't remove the BLOCKMAP and CONT
tlucas 2018/07/09 13:23:57 Done.
148 # mock a generationInfo.json, as i would be created by manifoldjs
Sebastian Noack 2018/07/09 12:35:19 Why do we have to do that manually, now where we u
Sebastian Noack 2018/07/09 12:35:20 Typo: i -> it
tlucas 2018/07/09 13:23:57 We don't have to. For packaging the extension (li
149 generation_info = packager.getTemplate('generationInfo.json.tmpl')
150 with open(os.path.join(tmp_dir, 'edgeextension',
151 'generationInfo.json'),
152 'w') as fp:
153 fp.write(generation_info.render(
154 {'generation_time': datetime.datetime.now()}))
205 155
206 files.zip(outfile, compression=zipfile.ZIP_STORED) 156 # package app with manifoldjs
157 cmd = ['npm', 'run', '--silent', 'package-edge']
158
159 cmd_env = os.environ.copy()
160 cmd_env['SRC_FOLDER'] = tmp_dir
161 subprocess.check_call(cmd, env=cmd_env, cwd=os.path.dirname(__file__))
162
163 package = os.path.join(tmp_dir, 'edgeextension', 'package',
164 'edgeExtension.appx')
165
166 if isinstance(outfile, basestring):
167 shutil.copyfile(package, outfile)
168 else:
169 with open(package, 'rb') as fp:
Sebastian Noack 2018/07/09 12:35:20 Where is this code path coming from? It seems prev
tlucas 2018/07/09 13:23:57 The tests, or creating a devenv may pass a StringI
170 outfile.write(fp.read())
171 finally:
172 shutil.rmtree(tmp_dir, ignore_errors=True)
OLDNEW
« packager.py ('K') | « packager.py ('k') | templates/edge/AppxBlockMap.xml.tmpl » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld