python/mozbuild/mozpack/chrome/manifest.py

changeset 0
6474c204b198
     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')

mercurial