summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/installer/packager.py
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/mozapps/installer/packager.py')
-rw-r--r--toolkit/mozapps/installer/packager.py300
1 files changed, 300 insertions, 0 deletions
diff --git a/toolkit/mozapps/installer/packager.py b/toolkit/mozapps/installer/packager.py
new file mode 100644
index 0000000000..92d00d253b
--- /dev/null
+++ b/toolkit/mozapps/installer/packager.py
@@ -0,0 +1,300 @@
+# 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 collections import OrderedDict
+from createprecomplete import generate_precomplete
+import os
+import plistlib
+import six
+from six import StringIO
+import subprocess
+
+
+class PackagerFileFinder(FileFinder):
+ def get(self, path):
+ f = super(PackagerFileFinder, self).get(path)
+ # Normalize Info.plist files, and remove the MozillaDeveloper*Path
+ # entries which are only needed on unpackaged builds.
+ if mozpath.basename(path) == "Info.plist":
+ info = plistlib.load(f.open(), dict_type=OrderedDict)
+ info.pop("MozillaDeveloperObjPath", None)
+ info.pop("MozillaDeveloperRepoPath", None)
+ return GeneratedFile(plistlib.dumps(info, sort_keys=False))
+ return f
+
+
+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 = PackagerFileFinder(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()