forked from mirrors/gecko-dev
		
	At the beginning of the Python 3 migration (circa bug 1602540), we made an update to the interface of `mozpack/files.py` in the direction of aligning with Python 3's built-in `file` support; namely, that opening a file in text mode returns a stream of `str` (text), and that opening a file in binary mode returns a stream of `bytes`. This was deemed to be more trouble than it was worth. This patch undoes all of those changes to the interface in favor of moving back to the Python 2 style, where all files are bytestreams. Differential Revision: https://phabricator.services.mozilla.com/D75424
		
			
				
	
	
		
			249 lines
		
	
	
	
		
			8.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			249 lines
		
	
	
	
		
			8.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
# License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
						|
 | 
						|
from __future__ import absolute_import, unicode_literals, print_function
 | 
						|
 | 
						|
from mozpack.packager.formats import (
 | 
						|
    FlatFormatter,
 | 
						|
    JarFormatter,
 | 
						|
    OmniJarFormatter,
 | 
						|
)
 | 
						|
from mozpack.packager import (
 | 
						|
    preprocess_manifest,
 | 
						|
    preprocess,
 | 
						|
    Component,
 | 
						|
    SimpleManifestSink,
 | 
						|
)
 | 
						|
from mozpack.files import (
 | 
						|
    GeneratedFile,
 | 
						|
    FileFinder,
 | 
						|
    File,
 | 
						|
)
 | 
						|
from mozpack.copier import (
 | 
						|
    FileCopier,
 | 
						|
    Jarrer,
 | 
						|
)
 | 
						|
from mozpack.errors import errors
 | 
						|
from mozpack.files import ExecutableFile
 | 
						|
from mozpack.mozjar import JAR_BROTLI
 | 
						|
import mozpack.path as mozpath
 | 
						|
import buildconfig
 | 
						|
from argparse import ArgumentParser
 | 
						|
from createprecomplete import generate_precomplete
 | 
						|
import os
 | 
						|
import six
 | 
						|
from six import StringIO
 | 
						|
import subprocess
 | 
						|
 | 
						|
 | 
						|
class RemovedFiles(GeneratedFile):
 | 
						|
    '''
 | 
						|
    File class for removed-files. Is used as a preprocessor parser.
 | 
						|
    '''
 | 
						|
    def __init__(self, copier):
 | 
						|
        self.copier = copier
 | 
						|
        GeneratedFile.__init__(self, b'')
 | 
						|
 | 
						|
    def handle_line(self, f):
 | 
						|
        f = f.strip()
 | 
						|
        if not f:
 | 
						|
            return
 | 
						|
        if self.copier.contains(f):
 | 
						|
            errors.error('Removal of packaged file(s): %s' % f)
 | 
						|
        self.content += six.ensure_binary(f) + b'\n'
 | 
						|
 | 
						|
 | 
						|
def split_define(define):
 | 
						|
    '''
 | 
						|
    Give a VAR[=VAL] string, returns a (VAR, VAL) tuple, where VAL defaults to
 | 
						|
    1. Numeric VALs are returned as ints.
 | 
						|
    '''
 | 
						|
    if '=' in define:
 | 
						|
        name, value = define.split('=', 1)
 | 
						|
        try:
 | 
						|
            value = int(value)
 | 
						|
        except ValueError:
 | 
						|
            pass
 | 
						|
        return (name, value)
 | 
						|
    return (define, 1)
 | 
						|
 | 
						|
 | 
						|
class NoPkgFilesRemover(object):
 | 
						|
    '''
 | 
						|
    Formatter wrapper to handle NO_PKG_FILES.
 | 
						|
    '''
 | 
						|
    def __init__(self, formatter, has_manifest):
 | 
						|
        assert 'NO_PKG_FILES' in os.environ
 | 
						|
        self._formatter = formatter
 | 
						|
        self._files = os.environ['NO_PKG_FILES'].split()
 | 
						|
        if has_manifest:
 | 
						|
            self._error = errors.error
 | 
						|
            self._msg = 'NO_PKG_FILES contains file listed in manifest: %s'
 | 
						|
        else:
 | 
						|
            self._error = errors.warn
 | 
						|
            self._msg = 'Skipping %s'
 | 
						|
 | 
						|
    def add_base(self, base, *args):
 | 
						|
        self._formatter.add_base(base, *args)
 | 
						|
 | 
						|
    def add(self, path, content):
 | 
						|
        if not any(mozpath.match(path, spec) for spec in self._files):
 | 
						|
            self._formatter.add(path, content)
 | 
						|
        else:
 | 
						|
            self._error(self._msg % path)
 | 
						|
 | 
						|
    def add_manifest(self, entry):
 | 
						|
        self._formatter.add_manifest(entry)
 | 
						|
 | 
						|
    def contains(self, path):
 | 
						|
        return self._formatter.contains(path)
 | 
						|
 | 
						|
 | 
						|
def main():
 | 
						|
    parser = ArgumentParser()
 | 
						|
    parser.add_argument('-D', dest='defines', action='append',
 | 
						|
                        metavar="VAR[=VAL]", help='Define a variable')
 | 
						|
    parser.add_argument('--format', default='omni',
 | 
						|
                        help='Choose the chrome format for packaging ' +
 | 
						|
                        '(omni, jar or flat ; default: %(default)s)')
 | 
						|
    parser.add_argument('--removals', default=None,
 | 
						|
                        help='removed-files source file')
 | 
						|
    parser.add_argument('--ignore-errors', action='store_true', default=False,
 | 
						|
                        help='Transform errors into warnings.')
 | 
						|
    parser.add_argument('--ignore-broken-symlinks', action='store_true', default=False,
 | 
						|
                        help='Do not fail when processing broken symlinks.')
 | 
						|
    parser.add_argument('--minify', action='store_true', default=False,
 | 
						|
                        help='Make some files more compact while packaging')
 | 
						|
    parser.add_argument('--minify-js', action='store_true',
 | 
						|
                        help='Minify JavaScript files while packaging.')
 | 
						|
    parser.add_argument('--js-binary',
 | 
						|
                        help='Path to js binary. This is used to verify '
 | 
						|
                        'minified JavaScript. If this is not defined, '
 | 
						|
                        'minification verification will not be performed.')
 | 
						|
    parser.add_argument('--jarlog', default='', help='File containing jar ' +
 | 
						|
                        'access logs')
 | 
						|
    parser.add_argument('--compress', choices=('none', 'deflate', 'brotli'),
 | 
						|
                        default='deflate',
 | 
						|
                        help='Use given jar compression (default: deflate)')
 | 
						|
    parser.add_argument('manifest', default=None, nargs='?',
 | 
						|
                        help='Manifest file name')
 | 
						|
    parser.add_argument('source', help='Source directory')
 | 
						|
    parser.add_argument('destination', help='Destination directory')
 | 
						|
    parser.add_argument('--non-resource', nargs='+', metavar='PATTERN',
 | 
						|
                        default=[],
 | 
						|
                        help='Extra files not to be considered as resources')
 | 
						|
    args = parser.parse_args()
 | 
						|
 | 
						|
    defines = dict(buildconfig.defines['ALLDEFINES'])
 | 
						|
    if args.ignore_errors:
 | 
						|
        errors.ignore_errors()
 | 
						|
 | 
						|
    if args.defines:
 | 
						|
        for name, value in [split_define(d) for d in args.defines]:
 | 
						|
            defines[name] = value
 | 
						|
 | 
						|
    compress = {
 | 
						|
        'none': False,
 | 
						|
        'deflate': True,
 | 
						|
        'brotli': JAR_BROTLI,
 | 
						|
    }[args.compress]
 | 
						|
 | 
						|
    copier = FileCopier()
 | 
						|
    if args.format == 'flat':
 | 
						|
        formatter = FlatFormatter(copier)
 | 
						|
    elif args.format == 'jar':
 | 
						|
        formatter = JarFormatter(copier, compress=compress)
 | 
						|
    elif args.format == 'omni':
 | 
						|
        formatter = OmniJarFormatter(copier,
 | 
						|
                                     buildconfig.substs['OMNIJAR_NAME'],
 | 
						|
                                     compress=compress,
 | 
						|
                                     non_resources=args.non_resource)
 | 
						|
    else:
 | 
						|
        errors.fatal('Unknown format: %s' % args.format)
 | 
						|
 | 
						|
    # Adjust defines according to the requested format.
 | 
						|
    if isinstance(formatter, OmniJarFormatter):
 | 
						|
        defines['MOZ_OMNIJAR'] = 1
 | 
						|
    elif 'MOZ_OMNIJAR' in defines:
 | 
						|
        del defines['MOZ_OMNIJAR']
 | 
						|
 | 
						|
    respath = ''
 | 
						|
    if 'RESPATH' in defines:
 | 
						|
        respath = SimpleManifestSink.normalize_path(defines['RESPATH'])
 | 
						|
    while respath.startswith('/'):
 | 
						|
        respath = respath[1:]
 | 
						|
 | 
						|
    with errors.accumulate():
 | 
						|
        finder_args = dict(
 | 
						|
            minify=args.minify,
 | 
						|
            minify_js=args.minify_js,
 | 
						|
            ignore_broken_symlinks=args.ignore_broken_symlinks,
 | 
						|
        )
 | 
						|
        if args.js_binary:
 | 
						|
            finder_args['minify_js_verify_command'] = [
 | 
						|
                args.js_binary,
 | 
						|
                os.path.join(os.path.abspath(os.path.dirname(__file__)),
 | 
						|
                    'js-compare-ast.js')
 | 
						|
            ]
 | 
						|
        finder = FileFinder(args.source, find_executables=True,
 | 
						|
                            **finder_args)
 | 
						|
        if 'NO_PKG_FILES' in os.environ:
 | 
						|
            sinkformatter = NoPkgFilesRemover(formatter,
 | 
						|
                                              args.manifest is not None)
 | 
						|
        else:
 | 
						|
            sinkformatter = formatter
 | 
						|
        sink = SimpleManifestSink(finder, sinkformatter)
 | 
						|
        if args.manifest:
 | 
						|
            preprocess_manifest(sink, args.manifest, defines)
 | 
						|
        else:
 | 
						|
            sink.add(Component(''), 'bin/*')
 | 
						|
        sink.close(args.manifest is not None)
 | 
						|
 | 
						|
        if args.removals:
 | 
						|
            removals_in = StringIO(open(args.removals).read())
 | 
						|
            removals_in.name = args.removals
 | 
						|
            removals = RemovedFiles(copier)
 | 
						|
            preprocess(removals_in, removals, defines)
 | 
						|
            copier.add(mozpath.join(respath, 'removed-files'), removals)
 | 
						|
 | 
						|
    # If a pdb file is present and we were instructed to copy it, include it.
 | 
						|
    # Run on all OSes to capture MinGW builds
 | 
						|
    if buildconfig.substs.get('MOZ_COPY_PDBS'):
 | 
						|
        # We want to mutate the copier while we're iterating through it, so copy
 | 
						|
        # the items to a list first.
 | 
						|
        copier_items = [(p, f) for p, f in copier]
 | 
						|
        for p, f in copier_items:
 | 
						|
            if isinstance(f, ExecutableFile):
 | 
						|
                pdbname = os.path.splitext(f.inputs()[0])[0] + '.pdb'
 | 
						|
                if os.path.exists(pdbname):
 | 
						|
                    copier.add(os.path.basename(pdbname), File(pdbname))
 | 
						|
 | 
						|
    # Setup preloading
 | 
						|
    if args.jarlog:
 | 
						|
        if not os.path.exists(args.jarlog):
 | 
						|
            raise Exception('Cannot find jar log: %s' % args.jarlog)
 | 
						|
        omnijars = []
 | 
						|
        if isinstance(formatter, OmniJarFormatter):
 | 
						|
            omnijars = [mozpath.join(base, buildconfig.substs['OMNIJAR_NAME'])
 | 
						|
                        for base in sink.packager.get_bases(addons=False)]
 | 
						|
 | 
						|
        from mozpack.mozjar import JarLog
 | 
						|
        log = JarLog(args.jarlog)
 | 
						|
        for p, f in copier:
 | 
						|
            if not isinstance(f, Jarrer):
 | 
						|
                continue
 | 
						|
            if respath:
 | 
						|
                p = mozpath.relpath(p, respath)
 | 
						|
            if p in log:
 | 
						|
                f.preload(log[p])
 | 
						|
            elif p in omnijars:
 | 
						|
                raise Exception('No jar log data for %s' % p)
 | 
						|
 | 
						|
    copier.copy(args.destination)
 | 
						|
    generate_precomplete(os.path.normpath(os.path.join(args.destination,
 | 
						|
                                                       respath)))
 | 
						|
 | 
						|
 | 
						|
if __name__ == '__main__':
 | 
						|
    main()
 |