Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 from mozpack.chrome.manifest import (
6 Manifest,
7 ManifestInterfaces,
8 ManifestChrome,
9 ManifestBinaryComponent,
10 ManifestResource,
11 )
12 from urlparse import urlparse
13 import mozpack.path
14 from mozpack.files import (
15 ManifestFile,
16 XPTFile,
17 )
18 from mozpack.copier import (
19 FileRegistry,
20 Jarrer,
21 )
23 STARTUP_CACHE_PATHS = [
24 'jsloader',
25 'jssubloader',
26 ]
28 '''
29 Formatters are classes receiving packaging instructions and creating the
30 appropriate package layout.
32 There are three distinct formatters, each handling one of the different chrome
33 formats:
34 - flat: essentially, copies files from the source with the same file system
35 layout. Manifests entries are grouped in a single manifest per directory,
36 as well as XPT interfaces.
37 - jar: chrome content is packaged in jar files.
38 - omni: chrome content, modules, non-binary components, and many other
39 elements are packaged in an omnijar file for each base directory.
41 The base interface provides the following methods:
42 - add_base(path)
43 Register a base directory for an application or GRE. Base directories
44 usually contain a root manifest (manifests not included in any other
45 manifest) named chrome.manifest.
46 - add(path, content)
47 Add the given content (BaseFile instance) at the given virtual path
48 - add_interfaces(path, content)
49 Add the given content (BaseFile instance) and link it to other
50 interfaces in the parent directory of the given virtual path.
51 - add_manifest(entry)
52 Add a ManifestEntry.
53 - contains(path)
54 Returns whether the given virtual path is known of the formatter.
56 The virtual paths mentioned above are paths as they would be with a flat
57 chrome.
59 Formatters all take a FileCopier instance they will fill with the packaged
60 data.
61 '''
64 class FlatFormatter(object):
65 '''
66 Formatter for the flat package format.
67 '''
68 def __init__(self, copier):
69 assert isinstance(copier, FileRegistry)
70 self.copier = copier
71 self._bases = ['']
72 self._frozen_bases = False
74 def add_base(self, base):
75 # Only allow to add a base directory before calls to _get_base()
76 assert not self._frozen_bases
77 if not base in self._bases:
78 self._bases.append(base)
80 def _get_base(self, path):
81 '''
82 Return the deepest base directory containing the given path.
83 '''
84 self._frozen_bases = True
85 return mozpack.path.basedir(path, self._bases)
87 def add(self, path, content):
88 self.copier.add(path, content)
90 def add_manifest(self, entry):
91 # Store manifest entries in a single manifest per directory, named
92 # after their parent directory, except for root manifests, all named
93 # chrome.manifest.
94 base = self._get_base(entry.base)
95 if entry.base == base:
96 name = 'chrome'
97 else:
98 name = mozpack.path.basename(entry.base)
99 path = mozpack.path.normpath(mozpack.path.join(entry.base,
100 '%s.manifest' % name))
101 if not self.copier.contains(path):
102 assert mozpack.path.basedir(entry.base, [base]) == base
103 # Add a reference to the manifest file in the parent manifest, if
104 # the manifest file is not a root manifest.
105 if len(entry.base) > len(base):
106 parent = mozpack.path.dirname(entry.base)
107 relbase = mozpack.path.basename(entry.base)
108 relpath = mozpack.path.join(relbase,
109 mozpack.path.basename(path))
110 FlatFormatter.add_manifest(self, Manifest(parent, relpath))
111 self.copier.add(path, ManifestFile(entry.base))
112 self.copier[path].add(entry)
114 def add_interfaces(self, path, content):
115 # Interfaces in the same directory are all linked together in an
116 # interfaces.xpt file.
117 interfaces_path = mozpack.path.join(mozpack.path.dirname(path),
118 'interfaces.xpt')
119 if not self.copier.contains(interfaces_path):
120 FlatFormatter.add_manifest(self, ManifestInterfaces(
121 mozpack.path.dirname(path), 'interfaces.xpt'))
122 self.copier.add(interfaces_path, XPTFile())
123 self.copier[interfaces_path].add(content)
125 def contains(self, path):
126 assert '*' not in path
127 return self.copier.contains(path)
130 class JarFormatter(FlatFormatter):
131 '''
132 Formatter for the jar package format. Assumes manifest entries related to
133 chrome are registered before the chrome data files are added. Also assumes
134 manifest entries for resources are registered after chrome manifest
135 entries.
136 '''
137 def __init__(self, copier, compress=True, optimize=True):
138 FlatFormatter.__init__(self, copier)
139 self._chrome = set()
140 self._frozen_chrome = False
141 self._compress = compress
142 self._optimize = optimize
144 def _chromepath(self, path):
145 '''
146 Return the chrome base directory under which the given path is. Used to
147 detect under which .jar (if any) the path should go.
148 '''
149 self._frozen_chrome = True
150 return mozpack.path.basedir(path, self._chrome)
152 def add(self, path, content):
153 chrome = self._chromepath(path)
154 if chrome:
155 jar = chrome + '.jar'
156 if not self.copier.contains(jar):
157 self.copier.add(jar, Jarrer(self._compress, self._optimize))
158 if not self.copier[jar].contains(mozpack.path.relpath(path,
159 chrome)):
160 self.copier[jar].add(mozpack.path.relpath(path, chrome),
161 content)
162 else:
163 FlatFormatter.add(self, path, content)
165 def _jarize(self, entry, relpath):
166 '''
167 Transform a manifest entry in one pointing to chrome data in a jar.
168 Return the corresponding chrome path and the new entry.
169 '''
170 base = entry.base
171 basepath = mozpack.path.split(relpath)[0]
172 chromepath = mozpack.path.join(base, basepath)
173 entry = entry.rebase(chromepath) \
174 .move(mozpack.path.join(base, 'jar:%s.jar!' % basepath)) \
175 .rebase(base)
176 return chromepath, entry
178 def add_manifest(self, entry):
179 if isinstance(entry, ManifestChrome) and \
180 not urlparse(entry.relpath).scheme:
181 chromepath, entry = self._jarize(entry, entry.relpath)
182 assert not self._frozen_chrome
183 self._chrome.add(chromepath)
184 elif isinstance(entry, ManifestResource) and \
185 not urlparse(entry.target).scheme:
186 chromepath, new_entry = self._jarize(entry, entry.target)
187 if chromepath in self._chrome:
188 entry = new_entry
189 FlatFormatter.add_manifest(self, entry)
191 def contains(self, path):
192 assert '*' not in path
193 chrome = self._chromepath(path)
194 if not chrome:
195 return self.copier.contains(path)
196 if not self.copier.contains(chrome + '.jar'):
197 return False
198 return self.copier[chrome + '.jar']. \
199 contains(mozpack.path.relpath(path, chrome))
202 class OmniJarFormatter(FlatFormatter):
203 '''
204 Formatter for the omnijar package format.
205 '''
206 def __init__(self, copier, omnijar_name, compress=True, optimize=True,
207 non_resources=[]):
208 FlatFormatter.__init__(self, copier)
209 self.omnijars = {}
210 self._omnijar_name = omnijar_name
211 self._compress = compress
212 self._optimize = optimize
213 self._non_resources = non_resources
215 def _get_omnijar(self, path, create=True):
216 '''
217 Return the omnijar corresponding to the given path, its base directory
218 and the path translated to be under the omnijar..
219 '''
220 base = self._get_base(path)
221 if not base in self.omnijars:
222 if not create:
223 return None, '', path
224 omnijar = Jarrer(self._compress, self._optimize)
225 self.omnijars[base] = FlatFormatter(omnijar)
226 self.copier.add(mozpack.path.join(base, self._omnijar_name),
227 omnijar)
228 return self.omnijars[base], base, mozpack.path.relpath(path, base)
230 def add(self, path, content):
231 if self.is_resource(path):
232 formatter, base, path = self._get_omnijar(path)
233 else:
234 formatter = self
235 FlatFormatter.add(formatter, path, content)
237 def add_manifest(self, entry):
238 if isinstance(entry, ManifestBinaryComponent):
239 formatter, base = self, ''
240 else:
241 formatter, base, path = self._get_omnijar(entry.base)
242 entry = entry.move(mozpack.path.relpath(entry.base, base))
243 FlatFormatter.add_manifest(formatter, entry)
245 def add_interfaces(self, path, content):
246 formatter, base, path = self._get_omnijar(path)
247 FlatFormatter.add_interfaces(formatter, path, content)
249 def contains(self, path):
250 assert '*' not in path
251 if self.copier.contains(path):
252 return True
253 for base, copier in self.omnijars.iteritems():
254 if copier.contains(mozpack.path.relpath(path, base)):
255 return True
256 return False
258 def is_resource(self, path):
259 '''
260 Return whether the given path corresponds to a resource to be put in an
261 omnijar archive.
262 '''
263 base = self._get_base(path)
264 path = mozpack.path.relpath(path, base)
265 if any(mozpack.path.match(path, p.replace('*', '**'))
266 for p in self._non_resources):
267 return False
268 path = mozpack.path.split(path)
269 if path[0] == 'chrome':
270 return len(path) == 1 or path[1] != 'icons'
271 if path[0] == 'components':
272 return path[-1].endswith('.js')
273 if path[0] == 'res':
274 return len(path) == 1 or \
275 (path[1] != 'cursors' and path[1] != 'MainMenu.nib')
276 if path[0] == 'defaults':
277 return len(path) != 3 or \
278 not (path[2] == 'channel-prefs.js' and
279 path[1] in ['pref', 'preferences'])
280 return path[0] in [
281 'modules',
282 'greprefs.js',
283 'hyphenation',
284 'update.locale',
285 ] or path[0] in STARTUP_CACHE_PATHS