toolkit/mozapps/installer/packager.py

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 4
michael@0 5 from mozpack.packager.formats import (
michael@0 6 FlatFormatter,
michael@0 7 JarFormatter,
michael@0 8 OmniJarFormatter,
michael@0 9 )
michael@0 10 from mozpack.packager import (
michael@0 11 preprocess_manifest,
michael@0 12 preprocess,
michael@0 13 Component,
michael@0 14 SimpleManifestSink,
michael@0 15 )
michael@0 16 from mozpack.files import (
michael@0 17 GeneratedFile,
michael@0 18 FileFinder,
michael@0 19 File,
michael@0 20 )
michael@0 21 from mozpack.copier import (
michael@0 22 FileCopier,
michael@0 23 Jarrer,
michael@0 24 )
michael@0 25 from mozpack.errors import errors
michael@0 26 from mozpack.unify import UnifiedBuildFinder
michael@0 27 import mozpack.path
michael@0 28 import buildconfig
michael@0 29 from argparse import ArgumentParser
michael@0 30 from createprecomplete import generate_precomplete
michael@0 31 import os
michael@0 32 from StringIO import StringIO
michael@0 33 import subprocess
michael@0 34 import platform
michael@0 35
michael@0 36 # List of libraries to shlibsign.
michael@0 37 SIGN_LIBS = [
michael@0 38 'softokn3',
michael@0 39 'nssdbm3',
michael@0 40 'freebl3',
michael@0 41 'freebl_32fpu_3',
michael@0 42 'freebl_32int_3',
michael@0 43 'freebl_32int64_3',
michael@0 44 'freebl_64fpu_3',
michael@0 45 'freebl_64int_3',
michael@0 46 ]
michael@0 47
michael@0 48
michael@0 49 class ToolLauncher(object):
michael@0 50 '''
michael@0 51 Helper to execute tools like xpcshell with the appropriate environment.
michael@0 52 launcher = ToolLauncher()
michael@0 53 launcher.tooldir = '/path/to/tools'
michael@0 54 launcher.launch(['xpcshell', '-e', 'foo.js'])
michael@0 55 '''
michael@0 56 def __init__(self):
michael@0 57 self.tooldir = None
michael@0 58
michael@0 59 def launch(self, cmd, extra_linker_path=None, extra_env={}):
michael@0 60 '''
michael@0 61 Launch the given command, passed as a list. The first item in the
michael@0 62 command list is the program name, without a path and without a suffix.
michael@0 63 These are determined from the tooldir member and the BIN_SUFFIX value.
michael@0 64 An extra_linker_path may be passed to give an additional directory
michael@0 65 to add to the search paths for the dynamic linker.
michael@0 66 An extra_env dict may be passed to give additional environment
michael@0 67 variables to export when running the command.
michael@0 68 '''
michael@0 69 assert self.tooldir
michael@0 70 cmd[0] = os.path.join(self.tooldir, 'bin',
michael@0 71 cmd[0] + buildconfig.substs['BIN_SUFFIX'])
michael@0 72 if not extra_linker_path:
michael@0 73 extra_linker_path = os.path.join(self.tooldir, 'bin')
michael@0 74 env = dict(os.environ)
michael@0 75 for p in ['LD_LIBRARY_PATH', 'DYLD_LIBRARY_PATH']:
michael@0 76 if p in env:
michael@0 77 env[p] = extra_linker_path + ':' + env[p]
michael@0 78 else:
michael@0 79 env[p] = extra_linker_path
michael@0 80 for e in extra_env:
michael@0 81 env[e] = extra_env[e]
michael@0 82
michael@0 83 # Work around a bug in Python 2.7.2 and lower where unicode types in
michael@0 84 # environment variables aren't handled by subprocess.
michael@0 85 for k, v in env.items():
michael@0 86 if isinstance(v, unicode):
michael@0 87 env[k] = v.encode('utf-8')
michael@0 88
michael@0 89 print >>errors.out, 'Executing', ' '.join(cmd)
michael@0 90 errors.out.flush()
michael@0 91 return subprocess.call(cmd, env=env)
michael@0 92
michael@0 93 def can_launch(self):
michael@0 94 return self.tooldir is not None
michael@0 95
michael@0 96 launcher = ToolLauncher()
michael@0 97
michael@0 98
michael@0 99 class LibSignFile(File):
michael@0 100 '''
michael@0 101 File class for shlibsign signatures.
michael@0 102 '''
michael@0 103 def copy(self, dest, skip_if_older=True):
michael@0 104 assert isinstance(dest, basestring)
michael@0 105 # os.path.getmtime returns a result in seconds with precision up to the
michael@0 106 # microsecond. But microsecond is too precise because shutil.copystat
michael@0 107 # only copies milliseconds, and seconds is not enough precision.
michael@0 108 if os.path.exists(dest) and skip_if_older and \
michael@0 109 int(os.path.getmtime(self.path) * 1000) <= \
michael@0 110 int(os.path.getmtime(dest) * 1000):
michael@0 111 return False
michael@0 112 if launcher.launch(['shlibsign', '-v', '-o', dest, '-i', self.path]):
michael@0 113 errors.fatal('Error while signing %s' % self.path)
michael@0 114
michael@0 115
michael@0 116 def precompile_cache(formatter, source_path, gre_path, app_path):
michael@0 117 '''
michael@0 118 Create startup cache for the given application directory, using the
michael@0 119 given GRE path.
michael@0 120 - formatter is a Formatter instance where to add the startup cache.
michael@0 121 - source_path is the base path of the package.
michael@0 122 - gre_path is the GRE path, relative to source_path.
michael@0 123 - app_path is the application path, relative to source_path.
michael@0 124 Startup cache for all resources under resource://app/ are generated,
michael@0 125 except when gre_path == app_path, in which case it's under
michael@0 126 resource://gre/.
michael@0 127 '''
michael@0 128 from tempfile import mkstemp
michael@0 129 source_path = os.path.abspath(source_path)
michael@0 130 if app_path != gre_path:
michael@0 131 resource = 'app'
michael@0 132 else:
michael@0 133 resource = 'gre'
michael@0 134 app_path = os.path.join(source_path, app_path)
michael@0 135 gre_path = os.path.join(source_path, gre_path)
michael@0 136
michael@0 137 fd, cache = mkstemp('.zip')
michael@0 138 os.close(fd)
michael@0 139 os.remove(cache)
michael@0 140
michael@0 141 # For VC12, make sure we can find the right bitness of pgort120.dll
michael@0 142 env = os.environ.copy()
michael@0 143 if 'VS120COMNTOOLS' in env and not buildconfig.substs['HAVE_64BIT_OS']:
michael@0 144 vc12dir = os.path.abspath(os.path.join(env['VS120COMNTOOLS'],
michael@0 145 '../../VC/bin'))
michael@0 146 if os.path.exists(vc12dir):
michael@0 147 env['PATH'] = vc12dir + ';' + env['PATH']
michael@0 148
michael@0 149 try:
michael@0 150 if launcher.launch(['xpcshell', '-g', gre_path, '-a', app_path,
michael@0 151 '-f', os.path.join(os.path.dirname(__file__),
michael@0 152 'precompile_cache.js'),
michael@0 153 '-e', 'precompile_startupcache("resource://%s/");'
michael@0 154 % resource],
michael@0 155 extra_linker_path=gre_path,
michael@0 156 extra_env={'MOZ_STARTUP_CACHE': cache,
michael@0 157 'PATH': env['PATH']}):
michael@0 158 errors.fatal('Error while running startup cache precompilation')
michael@0 159 return
michael@0 160 from mozpack.mozjar import JarReader
michael@0 161 jar = JarReader(cache)
michael@0 162 resource = '/resource/%s/' % resource
michael@0 163 for f in jar:
michael@0 164 if resource in f.filename:
michael@0 165 path = f.filename[f.filename.index(resource) + len(resource):]
michael@0 166 if formatter.contains(path):
michael@0 167 formatter.add(f.filename, GeneratedFile(f.read()))
michael@0 168 jar.close()
michael@0 169 finally:
michael@0 170 if os.path.exists(cache):
michael@0 171 os.remove(cache)
michael@0 172
michael@0 173
michael@0 174 class RemovedFiles(GeneratedFile):
michael@0 175 '''
michael@0 176 File class for removed-files. Is used as a preprocessor parser.
michael@0 177 '''
michael@0 178 def __init__(self, copier):
michael@0 179 self.copier = copier
michael@0 180 GeneratedFile.__init__(self, '')
michael@0 181
michael@0 182 def handle_line(self, str):
michael@0 183 f = str.strip()
michael@0 184 if self.copier.contains(f):
michael@0 185 errors.error('Removal of packaged file(s): %s' % f)
michael@0 186 self.content += f + '\n'
michael@0 187
michael@0 188
michael@0 189 def split_define(define):
michael@0 190 '''
michael@0 191 Give a VAR[=VAL] string, returns a (VAR, VAL) tuple, where VAL defaults to
michael@0 192 1. Numeric VALs are returned as ints.
michael@0 193 '''
michael@0 194 if '=' in define:
michael@0 195 name, value = define.split('=', 1)
michael@0 196 try:
michael@0 197 value = int(value)
michael@0 198 except ValueError:
michael@0 199 pass
michael@0 200 return (name, value)
michael@0 201 return (define, 1)
michael@0 202
michael@0 203
michael@0 204 class NoPkgFilesRemover(object):
michael@0 205 '''
michael@0 206 Formatter wrapper to handle NO_PKG_FILES.
michael@0 207 '''
michael@0 208 def __init__(self, formatter, has_manifest):
michael@0 209 assert 'NO_PKG_FILES' in os.environ
michael@0 210 self._formatter = formatter
michael@0 211 self._files = os.environ['NO_PKG_FILES'].split()
michael@0 212 if has_manifest:
michael@0 213 self._error = errors.error
michael@0 214 self._msg = 'NO_PKG_FILES contains file listed in manifest: %s'
michael@0 215 else:
michael@0 216 self._error = errors.warn
michael@0 217 self._msg = 'Skipping %s'
michael@0 218
michael@0 219 def add_base(self, base):
michael@0 220 self._formatter.add_base(base)
michael@0 221
michael@0 222 def add(self, path, content):
michael@0 223 if not any(mozpack.path.match(path, spec) for spec in self._files):
michael@0 224 self._formatter.add(path, content)
michael@0 225 else:
michael@0 226 self._error(self._msg % path)
michael@0 227
michael@0 228 def add_manifest(self, entry):
michael@0 229 self._formatter.add_manifest(entry)
michael@0 230
michael@0 231 def add_interfaces(self, path, content):
michael@0 232 self._formatter.add_interfaces(path, content)
michael@0 233
michael@0 234 def contains(self, path):
michael@0 235 return self._formatter.contains(path)
michael@0 236
michael@0 237
michael@0 238 def main():
michael@0 239 parser = ArgumentParser()
michael@0 240 parser.add_argument('-D', dest='defines', action='append',
michael@0 241 metavar="VAR[=VAL]", help='Define a variable')
michael@0 242 parser.add_argument('--format', default='omni',
michael@0 243 help='Choose the chrome format for packaging ' +
michael@0 244 '(omni, jar or flat ; default: %(default)s)')
michael@0 245 parser.add_argument('--removals', default=None,
michael@0 246 help='removed-files source file')
michael@0 247 parser.add_argument('--ignore-errors', action='store_true', default=False,
michael@0 248 help='Transform errors into warnings.')
michael@0 249 parser.add_argument('--minify', action='store_true', default=False,
michael@0 250 help='Make some files more compact while packaging')
michael@0 251 parser.add_argument('--minify-js', action='store_true',
michael@0 252 help='Minify JavaScript files while packaging.')
michael@0 253 parser.add_argument('--js-binary',
michael@0 254 help='Path to js binary. This is used to verify '
michael@0 255 'minified JavaScript. If this is not defined, '
michael@0 256 'minification verification will not be performed.')
michael@0 257 parser.add_argument('--jarlog', default='', help='File containing jar ' +
michael@0 258 'access logs')
michael@0 259 parser.add_argument('--optimizejars', action='store_true', default=False,
michael@0 260 help='Enable jar optimizations')
michael@0 261 parser.add_argument('--unify', default='',
michael@0 262 help='Base directory of another build to unify with')
michael@0 263 parser.add_argument('manifest', default=None, nargs='?',
michael@0 264 help='Manifest file name')
michael@0 265 parser.add_argument('source', help='Source directory')
michael@0 266 parser.add_argument('destination', help='Destination directory')
michael@0 267 parser.add_argument('--non-resource', nargs='+', metavar='PATTERN',
michael@0 268 default=[],
michael@0 269 help='Extra files not to be considered as resources')
michael@0 270 args = parser.parse_args()
michael@0 271
michael@0 272 defines = dict(buildconfig.defines)
michael@0 273 if args.ignore_errors:
michael@0 274 errors.ignore_errors()
michael@0 275
michael@0 276 if args.defines:
michael@0 277 for name, value in [split_define(d) for d in args.defines]:
michael@0 278 defines[name] = value
michael@0 279
michael@0 280 copier = FileCopier()
michael@0 281 if args.format == 'flat':
michael@0 282 formatter = FlatFormatter(copier)
michael@0 283 elif args.format == 'jar':
michael@0 284 formatter = JarFormatter(copier, optimize=args.optimizejars)
michael@0 285 elif args.format == 'omni':
michael@0 286 formatter = OmniJarFormatter(copier,
michael@0 287 buildconfig.substs['OMNIJAR_NAME'],
michael@0 288 optimize=args.optimizejars,
michael@0 289 non_resources=args.non_resource)
michael@0 290 else:
michael@0 291 errors.fatal('Unknown format: %s' % args.format)
michael@0 292
michael@0 293 # Adjust defines according to the requested format.
michael@0 294 if isinstance(formatter, OmniJarFormatter):
michael@0 295 defines['MOZ_OMNIJAR'] = 1
michael@0 296 elif 'MOZ_OMNIJAR' in defines:
michael@0 297 del defines['MOZ_OMNIJAR']
michael@0 298
michael@0 299 binpath = ''
michael@0 300 if 'BINPATH' in defines:
michael@0 301 binpath = SimpleManifestSink.normalize_path(defines['BINPATH'])
michael@0 302 while binpath.startswith('/'):
michael@0 303 binpath = binpath[1:]
michael@0 304
michael@0 305 if args.unify:
michael@0 306 def is_native(path):
michael@0 307 path = os.path.abspath(path)
michael@0 308 return platform.machine() in mozpack.path.split(path)
michael@0 309
michael@0 310 # Invert args.unify and args.source if args.unify points to the
michael@0 311 # native architecture.
michael@0 312 args.source, args.unify = sorted([args.source, args.unify],
michael@0 313 key=is_native, reverse=True)
michael@0 314 if is_native(args.source):
michael@0 315 launcher.tooldir = args.source
michael@0 316 elif not buildconfig.substs['CROSS_COMPILE']:
michael@0 317 launcher.tooldir = buildconfig.substs['LIBXUL_DIST']
michael@0 318
michael@0 319 with errors.accumulate():
michael@0 320 finder_args = dict(
michael@0 321 minify=args.minify,
michael@0 322 minify_js=args.minify_js,
michael@0 323 )
michael@0 324 if args.js_binary:
michael@0 325 finder_args['minify_js_verify_command'] = [
michael@0 326 args.js_binary,
michael@0 327 os.path.join(os.path.abspath(os.path.dirname(__file__)),
michael@0 328 'js-compare-ast.js')
michael@0 329 ]
michael@0 330 if args.unify:
michael@0 331 finder = UnifiedBuildFinder(FileFinder(args.source),
michael@0 332 FileFinder(args.unify),
michael@0 333 **finder_args)
michael@0 334 else:
michael@0 335 finder = FileFinder(args.source, **finder_args)
michael@0 336 if 'NO_PKG_FILES' in os.environ:
michael@0 337 sinkformatter = NoPkgFilesRemover(formatter,
michael@0 338 args.manifest is not None)
michael@0 339 else:
michael@0 340 sinkformatter = formatter
michael@0 341 sink = SimpleManifestSink(finder, sinkformatter)
michael@0 342 if args.manifest:
michael@0 343 preprocess_manifest(sink, args.manifest, defines)
michael@0 344 else:
michael@0 345 sink.add(Component(''), 'bin/*')
michael@0 346 sink.close(args.manifest is not None)
michael@0 347
michael@0 348 if args.removals:
michael@0 349 lines = [l.lstrip() for l in open(args.removals).readlines()]
michael@0 350 removals_in = StringIO(''.join(lines))
michael@0 351 removals_in.name = args.removals
michael@0 352 removals = RemovedFiles(copier)
michael@0 353 preprocess(removals_in, removals, defines)
michael@0 354 copier.add(mozpack.path.join(binpath, 'removed-files'), removals)
michael@0 355
michael@0 356 # shlibsign libraries
michael@0 357 if launcher.can_launch():
michael@0 358 for lib in SIGN_LIBS:
michael@0 359 libbase = mozpack.path.join(binpath, '%s%s') \
michael@0 360 % (buildconfig.substs['DLL_PREFIX'], lib)
michael@0 361 libname = '%s%s' % (libbase, buildconfig.substs['DLL_SUFFIX'])
michael@0 362 if copier.contains(libname):
michael@0 363 copier.add(libbase + '.chk',
michael@0 364 LibSignFile(os.path.join(args.destination,
michael@0 365 libname)))
michael@0 366
michael@0 367 # Setup preloading
michael@0 368 if args.jarlog and os.path.exists(args.jarlog):
michael@0 369 from mozpack.mozjar import JarLog
michael@0 370 log = JarLog(args.jarlog)
michael@0 371 for p, f in copier:
michael@0 372 if not isinstance(f, Jarrer):
michael@0 373 continue
michael@0 374 key = JarLog.canonicalize(os.path.join(args.destination, p))
michael@0 375 if key in log:
michael@0 376 f.preload(log[key])
michael@0 377
michael@0 378 # Fill startup cache
michael@0 379 if isinstance(formatter, OmniJarFormatter) and launcher.can_launch():
michael@0 380 if buildconfig.substs['LIBXUL_SDK']:
michael@0 381 gre_path = mozpack.path.join(buildconfig.substs['LIBXUL_DIST'],
michael@0 382 'bin')
michael@0 383 else:
michael@0 384 gre_path = None
michael@0 385 for base in sorted([[p for p in [mozpack.path.join('bin', b), b]
michael@0 386 if os.path.exists(os.path.join(args.source, p))][0]
michael@0 387 for b in sink.packager.get_bases()]):
michael@0 388 if not gre_path:
michael@0 389 gre_path = base
michael@0 390 base_path = sink.normalize_path(base)
michael@0 391 if base_path in formatter.omnijars:
michael@0 392 precompile_cache(formatter.omnijars[base_path],
michael@0 393 args.source, gre_path, base)
michael@0 394
michael@0 395 copier.copy(args.destination)
michael@0 396 generate_precomplete(os.path.normpath(os.path.join(args.destination,
michael@0 397 binpath)))
michael@0 398
michael@0 399
michael@0 400 if __name__ == '__main__':
michael@0 401 main()

mercurial