1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/python/mozbuild/mozpack/chrome/manifest.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,365 @@ 1.4 +# This Source Code Form is subject to the terms of the Mozilla Public 1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.7 + 1.8 +import re 1.9 +import os 1.10 +from urlparse import urlparse 1.11 +import mozpack.path 1.12 +from mozpack.chrome.flags import Flags 1.13 +from mozpack.errors import errors 1.14 + 1.15 + 1.16 +class ManifestEntry(object): 1.17 + ''' 1.18 + Base class for all manifest entry types. 1.19 + Subclasses may define the following class or member variables: 1.20 + - localized: indicates whether the manifest entry is used for localized 1.21 + data. 1.22 + - type: the manifest entry type (e.g. 'content' in 1.23 + 'content global content/global/') 1.24 + - allowed_flags: a set of flags allowed to be defined for the given 1.25 + manifest entry type. 1.26 + 1.27 + A manifest entry is attached to a base path, defining where the manifest 1.28 + entry is bound to, and that is used to find relative paths defined in 1.29 + entries. 1.30 + ''' 1.31 + localized = False 1.32 + type = None 1.33 + allowed_flags = [ 1.34 + 'application', 1.35 + 'platformversion', 1.36 + 'os', 1.37 + 'osversion', 1.38 + 'abi', 1.39 + 'xpcnativewrappers', 1.40 + 'tablet', 1.41 + ] 1.42 + 1.43 + def __init__(self, base, *flags): 1.44 + ''' 1.45 + Initialize a manifest entry with the given base path and flags. 1.46 + ''' 1.47 + self.base = base 1.48 + self.flags = Flags(*flags) 1.49 + if not all(f in self.allowed_flags for f in self.flags): 1.50 + errors.fatal('%s unsupported for %s manifest entries' % 1.51 + (','.join(f for f in self.flags 1.52 + if not f in self.allowed_flags), self.type)) 1.53 + 1.54 + def serialize(self, *args): 1.55 + ''' 1.56 + Serialize the manifest entry. 1.57 + ''' 1.58 + entry = [self.type] + list(args) 1.59 + flags = str(self.flags) 1.60 + if flags: 1.61 + entry.append(flags) 1.62 + return ' '.join(entry) 1.63 + 1.64 + def __eq__(self, other): 1.65 + return self.base == other.base and str(self) == str(other) 1.66 + 1.67 + def __ne__(self, other): 1.68 + return not self.__eq__(other) 1.69 + 1.70 + def __repr__(self): 1.71 + return '<%s@%s>' % (str(self), self.base) 1.72 + 1.73 + def move(self, base): 1.74 + ''' 1.75 + Return a new manifest entry with a different base path. 1.76 + ''' 1.77 + return parse_manifest_line(base, str(self)) 1.78 + 1.79 + def rebase(self, base): 1.80 + ''' 1.81 + Return a new manifest entry with all relative paths defined in the 1.82 + entry relative to a new base directory. 1.83 + The base class doesn't define relative paths, so it is equivalent to 1.84 + move(). 1.85 + ''' 1.86 + return self.move(base) 1.87 + 1.88 + 1.89 +class ManifestEntryWithRelPath(ManifestEntry): 1.90 + ''' 1.91 + Abstract manifest entry type with a relative path definition. 1.92 + ''' 1.93 + def __init__(self, base, relpath, *flags): 1.94 + ManifestEntry.__init__(self, base, *flags) 1.95 + self.relpath = relpath 1.96 + 1.97 + def __str__(self): 1.98 + return self.serialize(self.relpath) 1.99 + 1.100 + def rebase(self, base): 1.101 + ''' 1.102 + Return a new manifest entry with all relative paths defined in the 1.103 + entry relative to a new base directory. 1.104 + ''' 1.105 + clone = ManifestEntry.rebase(self, base) 1.106 + clone.relpath = mozpack.path.rebase(self.base, base, self.relpath) 1.107 + return clone 1.108 + 1.109 + @property 1.110 + def path(self): 1.111 + return mozpack.path.normpath(mozpack.path.join(self.base, 1.112 + self.relpath)) 1.113 + 1.114 + 1.115 +class Manifest(ManifestEntryWithRelPath): 1.116 + ''' 1.117 + Class for 'manifest' entries. 1.118 + manifest some/path/to/another.manifest 1.119 + ''' 1.120 + type = 'manifest' 1.121 + 1.122 + 1.123 +class ManifestChrome(ManifestEntryWithRelPath): 1.124 + ''' 1.125 + Abstract class for chrome entries. 1.126 + ''' 1.127 + def __init__(self, base, name, relpath, *flags): 1.128 + ManifestEntryWithRelPath.__init__(self, base, relpath, *flags) 1.129 + self.name = name 1.130 + 1.131 + @property 1.132 + def location(self): 1.133 + return mozpack.path.join(self.base, self.relpath) 1.134 + 1.135 + 1.136 +class ManifestContent(ManifestChrome): 1.137 + ''' 1.138 + Class for 'content' entries. 1.139 + content global content/global/ 1.140 + ''' 1.141 + type = 'content' 1.142 + allowed_flags = ManifestChrome.allowed_flags + [ 1.143 + 'contentaccessible', 1.144 + 'platform', 1.145 + ] 1.146 + 1.147 + def __str__(self): 1.148 + return self.serialize(self.name, self.relpath) 1.149 + 1.150 + 1.151 +class ManifestMultiContent(ManifestChrome): 1.152 + ''' 1.153 + Abstract class for chrome entries with multiple definitions. 1.154 + Used for locale and skin entries. 1.155 + ''' 1.156 + type = None 1.157 + 1.158 + def __init__(self, base, name, id, relpath, *flags): 1.159 + ManifestChrome.__init__(self, base, name, relpath, *flags) 1.160 + self.id = id 1.161 + 1.162 + def __str__(self): 1.163 + return self.serialize(self.name, self.id, self.relpath) 1.164 + 1.165 + 1.166 +class ManifestLocale(ManifestMultiContent): 1.167 + ''' 1.168 + Class for 'locale' entries. 1.169 + locale global en-US content/en-US/ 1.170 + locale global fr content/fr/ 1.171 + ''' 1.172 + localized = True 1.173 + type = 'locale' 1.174 + 1.175 + 1.176 +class ManifestSkin(ManifestMultiContent): 1.177 + ''' 1.178 + Class for 'skin' entries. 1.179 + skin global classic/1.0 content/skin/classic/ 1.180 + ''' 1.181 + type = 'skin' 1.182 + 1.183 + 1.184 +class ManifestOverload(ManifestEntry): 1.185 + ''' 1.186 + Abstract class for chrome entries defining some kind of overloading. 1.187 + Used for overlay, override or style entries. 1.188 + ''' 1.189 + type = None 1.190 + 1.191 + def __init__(self, base, overloaded, overload, *flags): 1.192 + ManifestEntry.__init__(self, base, *flags) 1.193 + self.overloaded = overloaded 1.194 + self.overload = overload 1.195 + 1.196 + def __str__(self): 1.197 + return self.serialize(self.overloaded, self.overload) 1.198 + 1.199 + @property 1.200 + def localized(self): 1.201 + u = urlparse(self.overload) 1.202 + return u.scheme == 'chrome' and \ 1.203 + u.path.split('/')[0:2] == ['', 'locale'] 1.204 + 1.205 + 1.206 +class ManifestOverlay(ManifestOverload): 1.207 + ''' 1.208 + Class for 'overlay' entries. 1.209 + overlay chrome://global/content/viewSource.xul \ 1.210 + chrome://browser/content/viewSourceOverlay.xul 1.211 + ''' 1.212 + type = 'overlay' 1.213 + 1.214 + 1.215 +class ManifestStyle(ManifestOverload): 1.216 + ''' 1.217 + Class for 'style' entries. 1.218 + style chrome://global/content/customizeToolbar.xul \ 1.219 + chrome://browser/skin/ 1.220 + ''' 1.221 + type = 'style' 1.222 + 1.223 + 1.224 +class ManifestOverride(ManifestOverload): 1.225 + ''' 1.226 + Class for 'override' entries. 1.227 + override chrome://global/locale/netError.dtd \ 1.228 + chrome://browser/locale/netError.dtd 1.229 + ''' 1.230 + type = 'override' 1.231 + 1.232 + 1.233 +class ManifestResource(ManifestEntry): 1.234 + ''' 1.235 + Class for 'resource' entries. 1.236 + resource gre-resources toolkit/res/ 1.237 + resource services-sync resource://gre/modules/services-sync/ 1.238 + 1.239 + The target may be a relative path or a resource or chrome url. 1.240 + ''' 1.241 + type = 'resource' 1.242 + 1.243 + def __init__(self, base, name, target, *flags): 1.244 + ManifestEntry.__init__(self, base, *flags) 1.245 + self.name = name 1.246 + self.target = target 1.247 + 1.248 + def __str__(self): 1.249 + return self.serialize(self.name, self.target) 1.250 + 1.251 + def rebase(self, base): 1.252 + u = urlparse(self.target) 1.253 + if u.scheme and u.scheme != 'jar': 1.254 + return ManifestEntry.rebase(self, base) 1.255 + clone = ManifestEntry.rebase(self, base) 1.256 + clone.target = mozpack.path.rebase(self.base, base, self.target) 1.257 + return clone 1.258 + 1.259 + 1.260 +class ManifestBinaryComponent(ManifestEntryWithRelPath): 1.261 + ''' 1.262 + Class for 'binary-component' entries. 1.263 + binary-component some/path/to/a/component.dll 1.264 + ''' 1.265 + type = 'binary-component' 1.266 + 1.267 + 1.268 +class ManifestComponent(ManifestEntryWithRelPath): 1.269 + ''' 1.270 + Class for 'component' entries. 1.271 + component {b2bba4df-057d-41ea-b6b1-94a10a8ede68} foo.js 1.272 + ''' 1.273 + type = 'component' 1.274 + 1.275 + def __init__(self, base, cid, file, *flags): 1.276 + ManifestEntryWithRelPath.__init__(self, base, file, *flags) 1.277 + self.cid = cid 1.278 + 1.279 + def __str__(self): 1.280 + return self.serialize(self.cid, self.relpath) 1.281 + 1.282 + 1.283 +class ManifestInterfaces(ManifestEntryWithRelPath): 1.284 + ''' 1.285 + Class for 'interfaces' entries. 1.286 + interfaces foo.xpt 1.287 + ''' 1.288 + type = 'interfaces' 1.289 + 1.290 + 1.291 +class ManifestCategory(ManifestEntry): 1.292 + ''' 1.293 + Class for 'category' entries. 1.294 + category command-line-handler m-browser @mozilla.org/browser/clh; 1.295 + ''' 1.296 + type = 'category' 1.297 + 1.298 + def __init__(self, base, category, name, value, *flags): 1.299 + ManifestEntry.__init__(self, base, *flags) 1.300 + self.category = category 1.301 + self.name = name 1.302 + self.value = value 1.303 + 1.304 + def __str__(self): 1.305 + return self.serialize(self.category, self.name, self.value) 1.306 + 1.307 + 1.308 +class ManifestContract(ManifestEntry): 1.309 + ''' 1.310 + Class for 'contract' entries. 1.311 + contract @mozilla.org/foo;1 {b2bba4df-057d-41ea-b6b1-94a10a8ede68} 1.312 + ''' 1.313 + type = 'contract' 1.314 + 1.315 + def __init__(self, base, contractID, cid, *flags): 1.316 + ManifestEntry.__init__(self, base, *flags) 1.317 + self.contractID = contractID 1.318 + self.cid = cid 1.319 + 1.320 + def __str__(self): 1.321 + return self.serialize(self.contractID, self.cid) 1.322 + 1.323 +# All manifest classes by their type name. 1.324 +MANIFESTS_TYPES = dict([(c.type, c) for c in globals().values() 1.325 + if type(c) == type and issubclass(c, ManifestEntry) 1.326 + and hasattr(c, 'type') and c.type]) 1.327 + 1.328 +MANIFEST_RE = re.compile(r'\s*#.*$') 1.329 + 1.330 + 1.331 +def parse_manifest_line(base, line): 1.332 + ''' 1.333 + Parse a line from a manifest file with the given base directory and 1.334 + return the corresponding ManifestEntry instance. 1.335 + ''' 1.336 + # Remove comments 1.337 + cmd = MANIFEST_RE.sub('', line).strip().split() 1.338 + if not cmd: 1.339 + return None 1.340 + if not cmd[0] in MANIFESTS_TYPES: 1.341 + return errors.fatal('Unknown manifest directive: %s' % cmd[0]) 1.342 + return MANIFESTS_TYPES[cmd[0]](base, *cmd[1:]) 1.343 + 1.344 + 1.345 +def parse_manifest(root, path, fileobj=None): 1.346 + ''' 1.347 + Parse a manifest file. 1.348 + ''' 1.349 + base = mozpack.path.dirname(path) 1.350 + if root: 1.351 + path = os.path.normpath(os.path.abspath(os.path.join(root, path))) 1.352 + if not fileobj: 1.353 + fileobj = open(path) 1.354 + linenum = 0 1.355 + for line in fileobj: 1.356 + linenum += 1 1.357 + with errors.context(path, linenum): 1.358 + e = parse_manifest_line(base, line) 1.359 + if e: 1.360 + yield e 1.361 + 1.362 + 1.363 +def is_manifest(path): 1.364 + ''' 1.365 + Return whether the given path is that of a manifest file. 1.366 + ''' 1.367 + return path.endswith('.manifest') and not path.endswith('.CRT.manifest') \ 1.368 + and not path.endswith('.exe.manifest')