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

Delta Between Two Patch Sets: packagerEdge.py

Issue 29825555: Issue 6291 - add ManifoldJS packaging for Edge (Closed) Base URL: https://hg.adblockplus.org/buildtools/file/9a56d76cd951
Left Patch Set: Created July 11, 2018, 12:05 p.m.
Right Patch Set: Final patch set Created Aug. 9, 2018, 7:08 a.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.json ('k') | templates/edge/AppxBlockMap.xml.tmpl » ('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 json
6 import os 5 import os
7 import shutil 6 import shutil
8 from StringIO import StringIO 7 from StringIO import StringIO
9 import subprocess 8 import subprocess
10 import tempfile 9 import tempfile
10 from xml.etree import ElementTree
11 from zipfile import ZipFile 11 from zipfile import ZipFile
12 12
13 import packager 13 import packager
14 import packagerChrome 14 import packagerChrome
15 15
16 # Files and directories expected inside of the .APPX archive.
17 MANIFEST = 'appxmanifest.xml' 16 MANIFEST = 'appxmanifest.xml'
18 EXTENSION_DIR = 'Extension'
19 ASSETS_DIR = 'Assets' 17 ASSETS_DIR = 'Assets'
20
21 # Size of uncompressed block in the APPX block map.
22 BLOCKSIZE = 64 * 1024
23 18
24 defaultLocale = packagerChrome.defaultLocale 19 defaultLocale = packagerChrome.defaultLocale
25 20
26 21
27 def _get_template_for(filename): 22 def _get_template_for(filename):
28 return packager.getTemplate('edge/{}.tmpl'.format(filename)) 23 return packager.getTemplate('edge/{}.tmpl'.format(filename))
29 24
30 25
31 def load_translation(files, locale): 26 def register_xml_namespaces(manifest_path):
32 """Load translation strings for locale from files.""" 27 """Register namespaces of the given file, in order to preserve defaults."""
33 path = '{}/_locales/{}/messages.json'.format(EXTENSION_DIR, locale) 28 with open(manifest_path, 'r') as fp:
34 return json.loads(files[path]) 29 ns = dict([node for _, node in ElementTree.iterparse(
30 fp, events=['start-ns'])])
31 for prefix, uri in ns.items():
32 ElementTree.register_namespace(prefix, uri)
33
34 # Make the default namespace available in an xpath expression
35 ns['_d'] = ns['']
36
37 return ns
35 38
36 39
37 def get_appx_version(metadata, build_num): 40 def update_appx_manifest(manifest_path, base_dir, files, metadata,
38 """Get the version number for usage in AppxManifest.xml. 41 release_build):
42 namespaces = register_xml_namespaces(manifest_path)
39 43
40 As required by the Windows Store, the returned version string has four 44 v_min, v_max = metadata.get('compat', 'windows').split('/')
41 components, where the 3rd component is replaced with the build number
42 if available, and the 4th component is always zero (e.g. 1.2.1000.0).
43 """
44 components = metadata.get('general', 'version').split('.')[:3]
45 components.extend(['0'] * (4 - len(components)))
46 if build_num:
47 components[2] = build_num
48 return '.'.join(components)
49 45
46 filenames = []
50 47
51 def create_appx_manifest(params, files, build_num, release_build): 48 for name, path in metadata.items('appx_assets'):
52 """Create AppxManifest.xml.""" 49 path = os.path.join(base_dir, path)
53 params = dict(params) 50 icon_path = '{}/{}'.format(ASSETS_DIR, name)
54 metadata = params['metadata']
55 w = params['windows_version'] = {}
56 w['min'], w['max'] = metadata.get('compat', 'windows').split('/')
57 params['version'] = get_appx_version(metadata, build_num)
58 51
59 metadata_suffix = 'release' if release_build else 'devbuild' 52 files.read(path, icon_path)
60 app_extension_id = 'extension_id_' + metadata_suffix 53 filenames.append(icon_path)
61 if metadata.has_option('general', app_extension_id):
62 params['app_extension_id'] = metadata.get('general', app_extension_id)
63 else:
64 params['app_extension_id'] = 'EdgeExtension'
65 54
66 params['app_id'] = packager.get_app_id(release_build, metadata) 55 assets = packagerChrome.makeIcons(files, filenames)
67 56
68 translation = load_translation(files, defaultLocale) 57 author = metadata.get('general', 'author')
69 name_key = 'name' if release_build else 'name_devbuild'
70 params['display_name'] = translation[name_key]['message']
71 params['description'] = translation['description']['message']
72 58
73 for size in ['44', '50', '150']: 59 overrides = [
74 path = '{}/logo_{}.png'.format(ASSETS_DIR, size) 60 ('_d:Identity', None, [
75 if path not in files: 61 ('Name', packager.get_app_id(release_build, metadata)),
76 raise KeyError(path + ' is not found in files') 62 ('Publisher', metadata.get('general', 'publisher_id')),
77 params['logo_' + size] = path.replace('/', '\\') 63 ]),
64 ('_d:Properties/_d:PublisherDisplayName', author, []),
65 ('_d:Properties/_d:Logo', assets[50], []),
66 ('_d:Dependencies/_d:TargetDeviceFamily', None, [
67 ('MaxVersionTested', v_max),
68 ('MinVersion', v_min),
69 ]),
70 ('_d:Applications/_d:Application/uap:VisualElements', None, [
71 ('Square150x150Logo', assets[150]),
72 ('Square44x44Logo', assets[44]),
73 ]),
74 ]
78 75
79 template = _get_template_for(MANIFEST) 76 tree = ElementTree.parse(manifest_path)
80 return template.render(params).encode('utf-8') 77 root = tree.getroot()
81 78
79 for xpath, text, attributes in overrides:
80 element = root.find(xpath, namespaces)
81 if text:
82 element.text = text
83 for attr, value in attributes:
84 element.set(attr, value)
82 85
83 def move_files_to_extension(files): 86 tree.write(manifest_path, encoding='utf-8', xml_declaration=True)
84 """Move all files into `Extension` folder for APPX packaging."""
85 # We sort the files to ensure that 'Extension/xyz' is moved before 'xyz'.
86 # If 'xyz' is moved first, it would overwrite 'Extension/xyz' and its
87 # original content would be lost.
88 names = sorted(files.keys(), key=len, reverse=True)
89 for filename in names:
90 files['{}/{}'.format(EXTENSION_DIR, filename)] = files.pop(filename)
91 87
92 88
93 def createBuild(baseDir, type='edge', outFile=None, # noqa: preserve API. 89 def createBuild(baseDir, type='edge', outFile=None, # noqa: preserve API.
94 buildNum=None, releaseBuild=False, keyFile=None, 90 buildNum=None, releaseBuild=False, keyFile=None,
95 devenv=False): 91 devenv=False):
96 92
97 metadata = packager.readMetadata(baseDir, type) 93 metadata = packager.readMetadata(baseDir, type)
98 version = packager.getBuildVersion(baseDir, metadata, releaseBuild, 94 version = packager.getBuildVersion(baseDir, metadata, releaseBuild,
99 buildNum) 95 buildNum)
100 96
(...skipping 26 matching lines...) Expand all
127 files.preprocess(metadata.options('preprocess'), {'needsExt': True}) 123 files.preprocess(metadata.options('preprocess'), {'needsExt': True})
128 124
129 if metadata.has_section('import_locales'): 125 if metadata.has_section('import_locales'):
130 packagerChrome.import_locales(params, files) 126 packagerChrome.import_locales(params, files)
131 127
132 files['manifest.json'] = packagerChrome.createManifest(params, files) 128 files['manifest.json'] = packagerChrome.createManifest(params, files)
133 129
134 if devenv: 130 if devenv:
135 packagerChrome.add_devenv_requirements(files, metadata, params) 131 packagerChrome.add_devenv_requirements(files, metadata, params)
136 132
137 move_files_to_extension(files)
Sebastian Noack 2018/07/11 16:26:47 Since we skip the packaging for devenv now, there
tlucas 2018/07/12 09:31:54 Turns out this is not necessary anymore at all - r
138
139 if metadata.has_section('appx_assets'):
140 for name, path in metadata.items('appx_assets'):
141 path = os.path.join(baseDir, path)
142 files.read(path, '{}/{}'.format(ASSETS_DIR, name))
143
144 if not devenv:
tlucas 2018/07/11 12:18:03 It turned out that devenvs for Edge don't need the
Oleksandr 2018/07/11 14:23:46 I can confirm that for devenv only the `Extension`
145 files[MANIFEST] = create_appx_manifest(params, files,
Sebastian Noack 2018/07/11 16:26:47 As discussed before, I would not override the APPX
tlucas 2018/07/12 09:31:55 Done (new function update_appx_manifest)
146 buildNum, releaseBuild)
147
148 zipped = StringIO() 133 zipped = StringIO()
149 files.zip(zipped) 134 files.zip(zipped)
150 135
151 zipped.seek(0) 136 zipped.seek(0)
152 137
153 if devenv: 138 if devenv:
154 shutil.copyfileobj(zipped, outfile) 139 shutil.copyfileobj(zipped, outfile)
Sebastian Noack 2018/07/11 16:26:46 Don't we have to extract the contents for the deve
tlucas 2018/07/12 09:31:54 Yes - but that's done in build.py, with all 3 exte
155 return 140 return
156 141
157 tmp_dir = tempfile.mkdtemp('adblockplus_package') 142 tmp_dir = tempfile.mkdtemp('adblockplus_package')
158 src_dir = os.path.join(tmp_dir, 'src') 143 try:
159 ext_dir = os.path.join(tmp_dir, 'ext') 144 src_dir = os.path.join(tmp_dir, 'src')
145 ext_dir = os.path.join(tmp_dir, 'ext')
160 146
161 with ZipFile(zipped, 'r') as zip_file: 147 with ZipFile(zipped, 'r') as zip_file:
162 zip_file.extractall(src_dir) 148 zip_file.extractall(src_dir)
163 149
164 try:
Sebastian Noack 2018/07/11 16:26:46 This try-block should probably start right after t
tlucas 2018/07/12 09:31:54 Done.
165 cmd_env = os.environ.copy() 150 cmd_env = os.environ.copy()
166 cmd_env['SRC_FOLDER'] = src_dir 151 cmd_env['SRC_FOLDER'] = src_dir
167 cmd_env['EXT_FOLDER'] = ext_dir 152 cmd_env['EXT_FOLDER'] = ext_dir
168 153
169 manifold_folder = os.path.join(ext_dir, 'MSGname', 'edgeextension') 154 manifold_folder = os.path.join(ext_dir, 'MSGname', 'edgeextension')
170 manifest_folder = os.path.join(manifold_folder, 'manifest') 155 manifest_folder = os.path.join(manifold_folder, 'manifest')
171 asset_folder = os.path.join(manifest_folder, 'Assets') 156 asset_folder = os.path.join(manifest_folder, ASSETS_DIR)
172 157
173 # prepare the extension with manifoldjs 158 # prepare the extension with manifoldjs
174 cmd = ['npm', 'run', '--silent', 'build-edge'] 159 cmd = ['npm', 'run', '--silent', 'build-edge']
175 subprocess.check_call(cmd, env=cmd_env, cwd=os.path.dirname(__file__)) 160 subprocess.check_call(cmd, env=cmd_env, cwd=os.path.dirname(__file__))
176 161
177 # overwrite incomplete appxmanifest 162 # update incomplete appxmanifest
178 intermediate_manifest = os.path.join(manifest_folder, MANIFEST) 163 intermediate_manifest = os.path.join(manifest_folder, MANIFEST)
179 shutil.copyfile(os.path.join(src_dir, MANIFEST), intermediate_manifest) 164 update_appx_manifest(intermediate_manifest, baseDir, files, metadata,
165 releaseBuild)
180 166
181 # cleanup placeholders, copy actual images 167 # cleanup placeholders, copy actual images
182 shutil.rmtree(asset_folder) 168 shutil.rmtree(asset_folder)
183 os.mkdir(asset_folder) 169 os.mkdir(asset_folder)
184 if metadata.has_section('appx_assets'): 170 if metadata.has_section('appx_assets'):
185 for name, path in metadata.items('appx_assets'): 171 for name, path in metadata.items('appx_assets'):
186 path = os.path.join(baseDir, path) 172 path = os.path.join(baseDir, path)
187 target = os.path.join(asset_folder, name) 173 target = os.path.join(asset_folder, name)
188 shutil.copyfile(path, target) 174 shutil.copyfile(path, target)
189 175
190 # package app with manifoldjs 176 # package app with manifoldjs
191 cmd = ['npm', 'run', '--silent', 'package-edge'] 177 cmd = ['npm', 'run', '--silent', 'package-edge']
192 178
193 subprocess.check_call(cmd, env=cmd_env, cwd=os.path.dirname(__file__)) 179 subprocess.check_call(cmd, env=cmd_env, cwd=os.path.dirname(__file__))
194 180
195 package = os.path.join(manifold_folder, 'package', 181 package = os.path.join(manifold_folder, 'package',
196 'edgeExtension.appx') 182 'edgeExtension.appx')
197 183
198 if isinstance(outfile, basestring): 184 shutil.copyfile(package, outfile)
199 shutil.copyfile(package, outfile)
200 else:
201 with open(package, 'rb') as fp:
Sebastian Noack 2018/07/11 16:26:46 This is essentially dead code now, since only in c
tlucas 2018/07/12 09:31:55 That's not an assumption, at this point 'outfile'
202 shutil.copyfileobj(fp, outfile)
203 finally: 185 finally:
204 shutil.rmtree(tmp_dir, ignore_errors=True) 186 shutil.rmtree(tmp_dir, ignore_errors=True)
LEFTRIGHT

Powered by Google App Engine
This is Rietveld