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 9, 2018, 1:20 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 datetime
6 import json
7 import os 5 import os
8 import shutil 6 import shutil
9 from StringIO import StringIO 7 from StringIO import StringIO
10 import subprocess 8 import subprocess
11 import tempfile 9 import tempfile
10 from xml.etree import ElementTree
12 from zipfile import ZipFile 11 from zipfile import ZipFile
13 12
14 import packager 13 import packager
15 import packagerChrome 14 import packagerChrome
16 15
17 # Files and directories expected inside of the .APPX archive.
18 MANIFEST = 'appxmanifest.xml' 16 MANIFEST = 'appxmanifest.xml'
19 EXTENSION_DIR = 'Extension'
20 ASSETS_DIR = 'Assets' 17 ASSETS_DIR = 'Assets'
21
22 # Size of uncompressed block in the APPX block map.
23 BLOCKSIZE = 64 * 1024
24 18
25 defaultLocale = packagerChrome.defaultLocale 19 defaultLocale = packagerChrome.defaultLocale
26 20
27 21
28 def _get_template_for(filename): 22 def _get_template_for(filename):
29 return packager.getTemplate('edge/{}.tmpl'.format(filename)) 23 return packager.getTemplate('edge/{}.tmpl'.format(filename))
30 24
31 25
32 def load_translation(files, locale): 26 def register_xml_namespaces(manifest_path):
33 """Load translation strings for locale from files.""" 27 """Register namespaces of the given file, in order to preserve defaults."""
34 path = '{}/_locales/{}/messages.json'.format(EXTENSION_DIR, locale) 28 with open(manifest_path, 'r') as fp:
35 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
36 38
37 39
38 def get_appx_version(metadata, build_num): 40 def update_appx_manifest(manifest_path, base_dir, files, metadata,
39 """Get the version number for usage in AppxManifest.xml. 41 release_build):
42 namespaces = register_xml_namespaces(manifest_path)
40 43
41 As required by the Windows Store, the returned version string has four 44 v_min, v_max = metadata.get('compat', 'windows').split('/')
42 components, where the 3rd component is replaced with the build number
43 if available, and the 4th component is always zero (e.g. 1.2.1000.0).
44 """
45 components = metadata.get('general', 'version').split('.')[:3]
46 components.extend(['0'] * (4 - len(components)))
47 if build_num:
48 components[2] = build_num
49 return '.'.join(components)
50 45
46 filenames = []
51 47
52 def create_appx_manifest(params, files, build_num, release_build): 48 for name, path in metadata.items('appx_assets'):
53 """Create AppxManifest.xml.""" 49 path = os.path.join(base_dir, path)
54 params = dict(params) 50 icon_path = '{}/{}'.format(ASSETS_DIR, name)
55 metadata = params['metadata']
56 w = params['windows_version'] = {}
57 w['min'], w['max'] = metadata.get('compat', 'windows').split('/')
58 params['version'] = get_appx_version(metadata, build_num)
59 51
60 metadata_suffix = 'release' if release_build else 'devbuild' 52 files.read(path, icon_path)
61 app_extension_id = 'extension_id_' + metadata_suffix 53 filenames.append(icon_path)
62 if metadata.has_option('general', app_extension_id):
63 params['app_extension_id'] = metadata.get('general', app_extension_id)
64 else:
65 params['app_extension_id'] = 'EdgeExtension'
66 54
67 params['app_id'] = packager.get_app_id(release_build, metadata) 55 assets = packagerChrome.makeIcons(files, filenames)
68 56
69 translation = load_translation(files, defaultLocale) 57 author = metadata.get('general', 'author')
70 name_key = 'name' if release_build else 'name_devbuild'
71 params['display_name'] = translation[name_key]['message']
72 params['description'] = translation['description']['message']
73 58
74 for size in ['44', '50', '150']: 59 overrides = [
75 path = '{}/logo_{}.png'.format(ASSETS_DIR, size) 60 ('_d:Identity', None, [
76 if path not in files: 61 ('Name', packager.get_app_id(release_build, metadata)),
77 raise KeyError(path + ' is not found in files') 62 ('Publisher', metadata.get('general', 'publisher_id')),
78 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 ]
79 75
80 template = _get_template_for(MANIFEST) 76 tree = ElementTree.parse(manifest_path)
81 return template.render(params).encode('utf-8') 77 root = tree.getroot()
82 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)
83 85
84 def move_files_to_extension(files): 86 tree.write(manifest_path, encoding='utf-8', xml_declaration=True)
85 """Move all files into `Extension` folder for APPX packaging."""
86 # We sort the files to ensure that 'Extension/xyz' is moved before 'xyz'.
87 # If 'xyz' is moved first, it would overwrite 'Extension/xyz' and its
88 # original content would be lost.
89 names = sorted(files.keys(), key=len, reverse=True)
90 for filename in names:
91 files['{}/{}'.format(EXTENSION_DIR, filename)] = files.pop(filename)
92 87
93 88
94 def createBuild(baseDir, type='edge', outFile=None, # noqa: preserve API. 89 def createBuild(baseDir, type='edge', outFile=None, # noqa: preserve API.
95 buildNum=None, releaseBuild=False, keyFile=None, 90 buildNum=None, releaseBuild=False, keyFile=None,
96 devenv=False): 91 devenv=False):
97 92
98 metadata = packager.readMetadata(baseDir, type) 93 metadata = packager.readMetadata(baseDir, type)
99 version = packager.getBuildVersion(baseDir, metadata, releaseBuild, 94 version = packager.getBuildVersion(baseDir, metadata, releaseBuild,
100 buildNum) 95 buildNum)
101 96
(...skipping 26 matching lines...) Expand all
128 files.preprocess(metadata.options('preprocess'), {'needsExt': True}) 123 files.preprocess(metadata.options('preprocess'), {'needsExt': True})
129 124
130 if metadata.has_section('import_locales'): 125 if metadata.has_section('import_locales'):
131 packagerChrome.import_locales(params, files) 126 packagerChrome.import_locales(params, files)
132 127
133 files['manifest.json'] = packagerChrome.createManifest(params, files) 128 files['manifest.json'] = packagerChrome.createManifest(params, files)
134 129
135 if devenv: 130 if devenv:
136 packagerChrome.add_devenv_requirements(files, metadata, params) 131 packagerChrome.add_devenv_requirements(files, metadata, params)
137 132
138 move_files_to_extension(files)
139
140 if metadata.has_section('appx_assets'):
141 for name, path in metadata.items('appx_assets'):
142 path = os.path.join(baseDir, path)
143 files.read(path, '{}/{}'.format(ASSETS_DIR, name))
144
145 files[MANIFEST] = create_appx_manifest(params, files,
146 buildNum, releaseBuild)
147
148 zipped = StringIO() 133 zipped = StringIO()
149 files.zip(zipped) 134 files.zip(zipped)
150 135
136 zipped.seek(0)
137
138 if devenv:
139 shutil.copyfileobj(zipped, outfile)
140 return
141
151 tmp_dir = tempfile.mkdtemp('adblockplus_package') 142 tmp_dir = tempfile.mkdtemp('adblockplus_package')
152 cwtd = os.path.join(tmp_dir, 'edgeextension/manifest') 143 try:
144 src_dir = os.path.join(tmp_dir, 'src')
145 ext_dir = os.path.join(tmp_dir, 'ext')
153 146
154 zipped.seek(0) 147 with ZipFile(zipped, 'r') as zip_file:
155 with ZipFile(zipped, 'r') as zip_file: 148 zip_file.extractall(src_dir)
156 zip_file.extractall(cwtd)
157 149
158 try: 150 cmd_env = os.environ.copy()
159 # mock a generationInfo.json, as it would be created by manifoldjs 151 cmd_env['SRC_FOLDER'] = src_dir
160 generation_info = packager.getTemplate('generationInfo.json.tmpl') 152 cmd_env['EXT_FOLDER'] = ext_dir
161 with open(os.path.join(tmp_dir, 'edgeextension', 153
162 'generationInfo.json'), 154 manifold_folder = os.path.join(ext_dir, 'MSGname', 'edgeextension')
163 'w') as fp: 155 manifest_folder = os.path.join(manifold_folder, 'manifest')
164 fp.write(generation_info.render( 156 asset_folder = os.path.join(manifest_folder, ASSETS_DIR)
165 {'generation_time': datetime.datetime.now()})) 157
158 # prepare the extension with manifoldjs
159 cmd = ['npm', 'run', '--silent', 'build-edge']
160 subprocess.check_call(cmd, env=cmd_env, cwd=os.path.dirname(__file__))
161
162 # update incomplete appxmanifest
163 intermediate_manifest = os.path.join(manifest_folder, MANIFEST)
164 update_appx_manifest(intermediate_manifest, baseDir, files, metadata,
165 releaseBuild)
166
167 # cleanup placeholders, copy actual images
168 shutil.rmtree(asset_folder)
169 os.mkdir(asset_folder)
170 if metadata.has_section('appx_assets'):
171 for name, path in metadata.items('appx_assets'):
172 path = os.path.join(baseDir, path)
173 target = os.path.join(asset_folder, name)
174 shutil.copyfile(path, target)
166 175
167 # package app with manifoldjs 176 # package app with manifoldjs
168 cmd = ['npm', 'run', '--silent', 'package-edge'] 177 cmd = ['npm', 'run', '--silent', 'package-edge']
169 178
170 cmd_env = os.environ.copy()
171 cmd_env['SRC_FOLDER'] = tmp_dir
172 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__))
173 180
174 package = os.path.join(tmp_dir, 'edgeextension', 'package', 181 package = os.path.join(manifold_folder, 'package',
175 'edgeExtension.appx') 182 'edgeExtension.appx')
176 183
177 if isinstance(outfile, basestring): 184 shutil.copyfile(package, outfile)
178 shutil.copyfile(package, outfile)
179 else:
180 with open(package, 'rb') as fp:
181 outfile.write(fp.read())
Sebastian Noack 2018/07/09 14:30:55 Since you already use shutil, you might want to us
tlucas 2018/07/09 14:56:07 Done.
182 finally: 185 finally:
183 shutil.rmtree(tmp_dir, ignore_errors=True) 186 shutil.rmtree(tmp_dir, ignore_errors=True)
LEFTRIGHT

Powered by Google App Engine
This is Rietveld