python/mozbuild/mozpack/packager/formats.py

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     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

mercurial