123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- # 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/.
- import os
- import zipfile
- import simplejson as json
- from cuddlefish.util import filter_filenames, filter_dirnames
- class HarnessOptionAlreadyDefinedError(Exception):
- """You cannot use --harness-option on keys that already exist in
- harness-options.json"""
- ZIPSEP = "/" # always use "/" in zipfiles
- def make_zipfile_path(localroot, localpath):
- return ZIPSEP.join(localpath[len(localroot)+1:].split(os.sep))
- def mkzipdir(zf, path):
- dirinfo = zipfile.ZipInfo(path)
- dirinfo.external_attr = int("040755", 8) << 16L
- zf.writestr(dirinfo, "")
- def build_xpi(template_root_dir, manifest, xpi_path,
- harness_options, limit_to=None, extra_harness_options={},
- bundle_sdk=True, pkgdir=""):
- IGNORED_FILES = [".hgignore", ".DS_Store", "install.rdf",
- "application.ini", xpi_path]
- files_to_copy = {} # maps zipfile path to local-disk abspath
- dirs_to_create = set() # zipfile paths, no trailing slash
- zf = zipfile.ZipFile(xpi_path, "w", zipfile.ZIP_DEFLATED)
- open('.install.rdf', 'w').write(str(manifest))
- zf.write('.install.rdf', 'install.rdf')
- os.remove('.install.rdf')
- # Handle add-on icon
- if 'icon' in harness_options:
- zf.write(str(harness_options['icon']), 'icon.png')
- del harness_options['icon']
- if 'icon64' in harness_options:
- zf.write(str(harness_options['icon64']), 'icon64.png')
- del harness_options['icon64']
- # chrome.manifest
- if os.path.isfile(os.path.join(pkgdir, 'chrome.manifest')):
- files_to_copy['chrome.manifest'] = os.path.join(pkgdir, 'chrome.manifest')
- # chrome folder (would contain content, skin, and locale folders typically)
- folder = 'chrome'
- if os.path.exists(os.path.join(pkgdir, folder)):
- dirs_to_create.add('chrome')
- # cp -r folder
- abs_dirname = os.path.join(pkgdir, folder)
- for dirpath, dirnames, filenames in os.walk(abs_dirname):
- goodfiles = list(filter_filenames(filenames, IGNORED_FILES))
- dirnames[:] = filter_dirnames(dirnames)
- for dirname in dirnames:
- arcpath = make_zipfile_path(template_root_dir,
- os.path.join(dirpath, dirname))
- dirs_to_create.add(arcpath)
- for filename in goodfiles:
- abspath = os.path.join(dirpath, filename)
- arcpath = ZIPSEP.join(
- [folder,
- make_zipfile_path(abs_dirname, os.path.join(dirpath, filename)),
- ])
- files_to_copy[str(arcpath)] = str(abspath)
- # Handle simple-prefs
- if 'preferences' in harness_options:
- from options_xul import parse_options, validate_prefs
- validate_prefs(harness_options["preferences"])
- opts_xul = parse_options(harness_options["preferences"],
- harness_options["jetpackID"],
- harness_options["preferencesBranch"])
- open('.options.xul', 'wb').write(opts_xul.encode("utf-8"))
- zf.write('.options.xul', 'options.xul')
- os.remove('.options.xul')
- from options_defaults import parse_options_defaults
- prefs_js = parse_options_defaults(harness_options["preferences"],
- harness_options["preferencesBranch"])
- open('.prefs.js', 'wb').write(prefs_js.encode("utf-8"))
- else:
- open('.prefs.js', 'wb').write("")
- zf.write('.prefs.js', 'defaults/preferences/prefs.js')
- os.remove('.prefs.js')
- for dirpath, dirnames, filenames in os.walk(template_root_dir):
- filenames = list(filter_filenames(filenames, IGNORED_FILES))
- dirnames[:] = filter_dirnames(dirnames)
- for dirname in dirnames:
- arcpath = make_zipfile_path(template_root_dir,
- os.path.join(dirpath, dirname))
- dirs_to_create.add(arcpath)
- for filename in filenames:
- abspath = os.path.join(dirpath, filename)
- arcpath = make_zipfile_path(template_root_dir, abspath)
- files_to_copy[arcpath] = abspath
- # `packages` attribute contains a dictionnary of dictionnary
- # of all packages sections directories
- for packageName in harness_options['packages']:
- base_arcpath = ZIPSEP.join(['resources', packageName])
- # Eventually strip sdk files. We need to do that in addition to the
- # whilelist as the whitelist is only used for `cfx xpi`:
- if not bundle_sdk and packageName == 'addon-sdk':
- continue
- # Always write the top directory, even if it contains no files, since
- # the harness will try to access it.
- dirs_to_create.add(base_arcpath)
- for sectionName in harness_options['packages'][packageName]:
- abs_dirname = harness_options['packages'][packageName][sectionName]
- base_arcpath = ZIPSEP.join(['resources', packageName, sectionName])
- # Always write the top directory, even if it contains no files, since
- # the harness will try to access it.
- dirs_to_create.add(base_arcpath)
- # cp -r stuff from abs_dirname/ into ZIP/resources/RESOURCEBASE/
- for dirpath, dirnames, filenames in os.walk(abs_dirname):
- goodfiles = list(filter_filenames(filenames, IGNORED_FILES))
- dirnames[:] = filter_dirnames(dirnames)
- for filename in goodfiles:
- abspath = os.path.join(dirpath, filename)
- if limit_to is not None and abspath not in limit_to:
- continue # strip unused files
- arcpath = ZIPSEP.join(
- ['resources',
- packageName,
- sectionName,
- make_zipfile_path(abs_dirname,
- os.path.join(dirpath, filename)),
- ])
- files_to_copy[str(arcpath)] = str(abspath)
- del harness_options['packages']
- locales_json_data = {"locales": []}
- mkzipdir(zf, "locale/")
- for language in sorted(harness_options['locale']):
- locales_json_data["locales"].append(language)
- locale = harness_options['locale'][language]
- # Be carefull about strings, we need to always ensure working with UTF-8
- jsonStr = json.dumps(locale, indent=1, sort_keys=True, ensure_ascii=False)
- info = zipfile.ZipInfo('locale/' + language + '.json')
- info.external_attr = 0644 << 16L
- zf.writestr(info, jsonStr.encode( "utf-8" ))
- del harness_options['locale']
- jsonStr = json.dumps(locales_json_data, ensure_ascii=True) +"\n"
- info = zipfile.ZipInfo('locales.json')
- info.external_attr = 0644 << 16L
- zf.writestr(info, jsonStr.encode("utf-8"))
- # now figure out which directories we need: all retained files parents
- for arcpath in files_to_copy:
- bits = arcpath.split("/")
- for i in range(1,len(bits)):
- parentpath = ZIPSEP.join(bits[0:i])
- dirs_to_create.add(parentpath)
- # Create zipfile in alphabetical order, with each directory before its
- # files
- for name in sorted(dirs_to_create.union(set(files_to_copy))):
- if name in dirs_to_create:
- mkzipdir(zf, name+"/")
- if name in files_to_copy:
- zf.write(files_to_copy[name], name)
- # Add extra harness options
- harness_options = harness_options.copy()
- for key,value in extra_harness_options.items():
- if key in harness_options:
- msg = "Can't use --harness-option for existing key '%s'" % key
- raise HarnessOptionAlreadyDefinedError(msg)
- harness_options[key] = value
- # Write harness-options.json
- open('.options.json', 'w').write(json.dumps(harness_options, indent=1,
- sort_keys=True))
- zf.write('.options.json', 'harness-options.json')
- os.remove('.options.json')
- zf.close()
|