Left: | ||
Right: |
LEFT | RIGHT |
---|---|
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 |
11 from xml.etree import ElementTree | 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 MANIFEST = 'appxmanifest.xml' | 16 MANIFEST = 'appxmanifest.xml' |
17 ASSETS_DIR = 'Assets' | |
18 | 18 |
19 defaultLocale = packagerChrome.defaultLocale | 19 defaultLocale = packagerChrome.defaultLocale |
20 | 20 |
21 | 21 |
22 def _get_template_for(filename): | 22 def _get_template_for(filename): |
23 return packager.getTemplate('edge/{}.tmpl'.format(filename)) | 23 return packager.getTemplate('edge/{}.tmpl'.format(filename)) |
24 | |
25 | |
26 def load_translation(files, locale): | |
27 """Load translation strings for locale from files.""" | |
28 path = '_locales/{}/messages.json'.format(locale) | |
29 return json.loads(files[path]) | |
30 | 24 |
31 | 25 |
32 def register_xml_namespaces(manifest_path): | 26 def register_xml_namespaces(manifest_path): |
33 """Register namespaces of the given file, in order to preserve defaults.""" | 27 """Register namespaces of the given file, in order to preserve defaults.""" |
34 with open(manifest_path, 'r') as fp: | 28 with open(manifest_path, 'r') as fp: |
35 ns = dict([node for _, node in ElementTree.iterparse( | 29 ns = dict([node for _, node in ElementTree.iterparse( |
36 fp, events=['start-ns'])]) | 30 fp, events=['start-ns'])]) |
37 for prefix, uri in ns.items(): | 31 for prefix, uri in ns.items(): |
38 ElementTree.register_namespace(prefix, uri) | 32 ElementTree.register_namespace(prefix, uri) |
39 | 33 |
34 # Make the default namespace available in an xpath expression | |
35 ns['_d'] = ns[''] | |
36 | |
40 return ns | 37 return ns |
41 | 38 |
42 | 39 |
43 def update_appx_manifest(manifest_path, files, metadata, release_build): | 40 def update_appx_manifest(manifest_path, base_dir, files, metadata, |
41 release_build): | |
44 namespaces = register_xml_namespaces(manifest_path) | 42 namespaces = register_xml_namespaces(manifest_path) |
Sebastian Noack
2018/07/12 14:43:10
I wonder if this is necessary? What happens if you
tlucas
2018/07/19 12:30:27
This is necessary so that xml.etree.ElementTree.wr
| |
45 | 43 |
46 def traverse(current_elem, overwrite): | |
47 if isinstance(overwrite, dict): | |
48 for key, value in overwrite.items(): | |
49 if isinstance(key, tuple): | |
50 prefix, element = key | |
51 next_elem = current_elem.find( | |
52 '{{{}}}{}'.format(namespaces[prefix], element)) | |
53 traverse(next_elem, value) | |
54 else: | |
55 current_elem.attrib.update(overwrite) | |
56 else: | |
57 current_elem.text = overwrite | |
58 | |
59 translation = load_translation(files, defaultLocale) | |
60 name_key = 'name' if release_build else 'name_devbuild' | |
61 v_min, v_max = metadata.get('compat', 'windows').split('/') | 44 v_min, v_max = metadata.get('compat', 'windows').split('/') |
62 | 45 |
63 overwrite = { | 46 filenames = [] |
Sebastian Noack
2018/07/12 14:43:10
Instead of a dictionaries you might want to use li
tlucas
2018/07/19 12:30:28
Since order doesn't matter in the resulting XML (o
Sebastian Noack
2018/07/25 19:18:41
Originally I thought that nodes may be created for
tlucas
2018/08/08 09:35:54
It looks like adding the default namespaces with a
| |
64 ('', 'Identity'): { | 47 |
65 'Name': packager.get_app_id(release_build, metadata), | 48 for name, path in metadata.items('appx_assets'): |
66 'Publisher': metadata.get('general', 'publisher_id'), | 49 path = os.path.join(base_dir, path) |
67 }, | 50 icon_path = '{}/{}'.format(ASSETS_DIR, name) |
68 ('', 'Properties'): { | 51 |
69 ('', 'DisplayName'): translation[name_key]['message'], | 52 files.read(path, icon_path) |
Sebastian Noack
2018/07/12 14:43:10
It seems we can just leave the DisplayName alone.
tlucas
2018/07/19 12:30:27
True indeed, removed this.
| |
70 ('', 'PublisherDisplayName'): metadata.get('general', 'author'), | 53 filenames.append(icon_path) |
71 ('', 'Logo'): 'Assets/logo_50.png', | 54 |
72 }, | 55 assets = packagerChrome.makeIcons(files, filenames) |
73 ('', 'Dependencies'): { | 56 |
74 ('', 'TargetDeviceFamily'): { | 57 author = metadata.get('general', 'author') |
75 'MaxVersionTested': v_max, | 58 |
76 'MinVersion': v_min, | 59 overrides = [ |
77 }, | 60 ('_d:Identity', None, [ |
78 }, | 61 ('Name', packager.get_app_id(release_build, metadata)), |
79 ('', 'Applications'): { | 62 ('Publisher', metadata.get('general', 'publisher_id')), |
80 ('', 'Application'): { | 63 ]), |
81 ('uap', 'VisualElements'): { | 64 ('_d:Properties/_d:PublisherDisplayName', author, []), |
Sebastian Noack
2018/07/12 14:43:10
Perhaps this structure is more readable if keys ar
tlucas
2018/07/19 12:30:27
You may be right about it being (only imho) a bit
Sebastian Noack
2018/07/25 19:18:41
What I meant is using "uap:VisualElements" as key
tlucas
2018/08/08 09:35:54
Done.
| |
82 'Square150x150Logo': 'Assets/logo_150.png', | 65 ('_d:Properties/_d:Logo', assets[50], []), |
83 'Square44x44Logo': 'Assets/logo_44.png', | 66 ('_d:Dependencies/_d:TargetDeviceFamily', None, [ |
Sebastian Noack
2018/07/12 14:43:10
Instead of hard-coding the assets, we could identi
tlucas
2018/07/19 12:30:27
Done.
| |
84 }, | 67 ('MaxVersionTested', v_max), |
85 }, | 68 ('MinVersion', v_min), |
86 }, | 69 ]), |
87 } | 70 ('_d:Applications/_d:Application/uap:VisualElements', None, [ |
71 ('Square150x150Logo', assets[150]), | |
72 ('Square44x44Logo', assets[44]), | |
73 ]), | |
74 ] | |
88 | 75 |
89 tree = ElementTree.parse(manifest_path) | 76 tree = ElementTree.parse(manifest_path) |
90 root = tree.getroot() | 77 root = tree.getroot() |
91 | 78 |
92 traverse(root, overwrite) | 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) | |
93 | 85 |
94 tree.write(manifest_path, encoding='utf-8', xml_declaration=True) | 86 tree.write(manifest_path, encoding='utf-8', xml_declaration=True) |
95 | 87 |
96 | 88 |
97 def createBuild(baseDir, type='edge', outFile=None, # noqa: preserve API. | 89 def createBuild(baseDir, type='edge', outFile=None, # noqa: preserve API. |
98 buildNum=None, releaseBuild=False, keyFile=None, | 90 buildNum=None, releaseBuild=False, keyFile=None, |
99 devenv=False): | 91 devenv=False): |
100 | 92 |
101 metadata = packager.readMetadata(baseDir, type) | 93 metadata = packager.readMetadata(baseDir, type) |
102 version = packager.getBuildVersion(baseDir, metadata, releaseBuild, | 94 version = packager.getBuildVersion(baseDir, metadata, releaseBuild, |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
154 | 146 |
155 with ZipFile(zipped, 'r') as zip_file: | 147 with ZipFile(zipped, 'r') as zip_file: |
156 zip_file.extractall(src_dir) | 148 zip_file.extractall(src_dir) |
157 | 149 |
158 cmd_env = os.environ.copy() | 150 cmd_env = os.environ.copy() |
159 cmd_env['SRC_FOLDER'] = src_dir | 151 cmd_env['SRC_FOLDER'] = src_dir |
160 cmd_env['EXT_FOLDER'] = ext_dir | 152 cmd_env['EXT_FOLDER'] = ext_dir |
161 | 153 |
162 manifold_folder = os.path.join(ext_dir, 'MSGname', 'edgeextension') | 154 manifold_folder = os.path.join(ext_dir, 'MSGname', 'edgeextension') |
163 manifest_folder = os.path.join(manifold_folder, 'manifest') | 155 manifest_folder = os.path.join(manifold_folder, 'manifest') |
164 asset_folder = os.path.join(manifest_folder, 'Assets') | 156 asset_folder = os.path.join(manifest_folder, ASSETS_DIR) |
165 | 157 |
166 # prepare the extension with manifoldjs | 158 # prepare the extension with manifoldjs |
167 cmd = ['npm', 'run', '--silent', 'build-edge'] | 159 cmd = ['npm', 'run', '--silent', 'build-edge'] |
168 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__)) |
169 | 161 |
170 # update incomplete appxmanifest | 162 # update incomplete appxmanifest |
171 intermediate_manifest = os.path.join(manifest_folder, MANIFEST) | 163 intermediate_manifest = os.path.join(manifest_folder, MANIFEST) |
172 update_appx_manifest(intermediate_manifest, files, metadata, | 164 update_appx_manifest(intermediate_manifest, baseDir, files, metadata, |
173 releaseBuild) | 165 releaseBuild) |
174 | 166 |
175 # cleanup placeholders, copy actual images | 167 # cleanup placeholders, copy actual images |
176 shutil.rmtree(asset_folder) | 168 shutil.rmtree(asset_folder) |
177 os.mkdir(asset_folder) | 169 os.mkdir(asset_folder) |
178 if metadata.has_section('appx_assets'): | 170 if metadata.has_section('appx_assets'): |
179 for name, path in metadata.items('appx_assets'): | 171 for name, path in metadata.items('appx_assets'): |
180 path = os.path.join(baseDir, path) | 172 path = os.path.join(baseDir, path) |
181 target = os.path.join(asset_folder, name) | 173 target = os.path.join(asset_folder, name) |
182 shutil.copyfile(path, target) | 174 shutil.copyfile(path, target) |
183 | 175 |
184 # package app with manifoldjs | 176 # package app with manifoldjs |
185 cmd = ['npm', 'run', '--silent', 'package-edge'] | 177 cmd = ['npm', 'run', '--silent', 'package-edge'] |
186 | 178 |
187 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__)) |
188 | 180 |
189 package = os.path.join(manifold_folder, 'package', | 181 package = os.path.join(manifold_folder, 'package', |
190 'edgeExtension.appx') | 182 'edgeExtension.appx') |
191 | 183 |
192 shutil.copyfile(package, outfile) | 184 shutil.copyfile(package, outfile) |
193 finally: | 185 finally: |
194 shutil.rmtree(tmp_dir, ignore_errors=True) | 186 shutil.rmtree(tmp_dir, ignore_errors=True) |
LEFT | RIGHT |