mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
886 lines
37 KiB
Python
886 lines
37 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/.
|
|
|
|
import sys
|
|
import os
|
|
import optparse
|
|
import webbrowser
|
|
import time
|
|
|
|
from copy import copy
|
|
import simplejson as json
|
|
from cuddlefish import packaging
|
|
from cuddlefish._version import get_versions
|
|
|
|
MOZRUNNER_BIN_NOT_FOUND = 'Mozrunner could not locate your binary'
|
|
MOZRUNNER_BIN_NOT_FOUND_HELP = """
|
|
I can't find the application binary in any of its default locations
|
|
on your system. Please specify one using the -b/--binary option.
|
|
"""
|
|
|
|
UPDATE_RDF_FILENAME = "%s.update.rdf"
|
|
XPI_FILENAME = "%s.xpi"
|
|
|
|
usage = """
|
|
%prog [options] command [command-specific options]
|
|
|
|
Supported Commands:
|
|
docs - view web-based documentation
|
|
init - create a sample addon in an empty directory
|
|
test - run tests
|
|
run - run program
|
|
xpi - generate an xpi
|
|
|
|
Internal Commands:
|
|
sdocs - export static documentation
|
|
testcfx - test the cfx tool
|
|
testex - test all example code
|
|
testpkgs - test all installed packages
|
|
testall - test whole environment
|
|
|
|
Experimental and internal commands and options are not supported and may be
|
|
changed or removed in the future.
|
|
"""
|
|
|
|
global_options = [
|
|
(("-v", "--verbose",), dict(dest="verbose",
|
|
help="enable lots of output",
|
|
action="store_true",
|
|
default=False)),
|
|
]
|
|
|
|
parser_groups = (
|
|
("Supported Command-Specific Options", [
|
|
(("", "--update-url",), dict(dest="update_url",
|
|
help="update URL in install.rdf",
|
|
metavar=None,
|
|
default=None,
|
|
cmds=['xpi'])),
|
|
(("", "--update-link",), dict(dest="update_link",
|
|
help="generate update.rdf",
|
|
metavar=None,
|
|
default=None,
|
|
cmds=['xpi'])),
|
|
(("-p", "--profiledir",), dict(dest="profiledir",
|
|
help=("profile directory to pass to "
|
|
"app"),
|
|
metavar=None,
|
|
default=None,
|
|
cmds=['test', 'run', 'testex',
|
|
'testpkgs', 'testall'])),
|
|
(("-b", "--binary",), dict(dest="binary",
|
|
help="path to app binary",
|
|
metavar=None,
|
|
default=None,
|
|
cmds=['test', 'run', 'testex', 'testpkgs',
|
|
'testall'])),
|
|
(("", "--binary-args",), dict(dest="cmdargs",
|
|
help=("additional arguments passed to the "
|
|
"binary"),
|
|
metavar=None,
|
|
default=None,
|
|
cmds=['run', 'test'])),
|
|
(("", "--dependencies",), dict(dest="dep_tests",
|
|
help="include tests for all deps",
|
|
action="store_true",
|
|
default=False,
|
|
cmds=['test', 'testex', 'testpkgs',
|
|
'testall'])),
|
|
(("", "--times",), dict(dest="iterations",
|
|
type="int",
|
|
help="number of times to run tests",
|
|
default=1,
|
|
cmds=['test', 'testex', 'testpkgs',
|
|
'testall'])),
|
|
(("-f", "--filter",), dict(dest="filter",
|
|
help=("only run tests whose filenames "
|
|
"match FILENAME and optionally "
|
|
"match TESTNAME, both regexps"),
|
|
metavar="FILENAME[:TESTNAME]",
|
|
default=None,
|
|
cmds=['test', 'testex', 'testpkgs',
|
|
'testall'])),
|
|
(("-g", "--use-config",), dict(dest="config",
|
|
help="use named config from local.json",
|
|
metavar=None,
|
|
default="default",
|
|
cmds=['test', 'run', 'xpi', 'testex',
|
|
'testpkgs', 'testall'])),
|
|
(("", "--templatedir",), dict(dest="templatedir",
|
|
help="XULRunner app/ext. template",
|
|
metavar=None,
|
|
default=None,
|
|
cmds=['run', 'xpi'])),
|
|
(("", "--package-path",), dict(dest="packagepath", action="append",
|
|
help="extra directories for package search",
|
|
metavar=None,
|
|
default=[],
|
|
cmds=['run', 'xpi', 'test'])),
|
|
(("", "--extra-packages",), dict(dest="extra_packages",
|
|
help=("extra packages to include, "
|
|
"comma-separated. Default is "
|
|
"'addon-sdk'."),
|
|
metavar=None,
|
|
default="addon-sdk",
|
|
cmds=['run', 'xpi', 'test', 'testex',
|
|
'testpkgs', 'testall',
|
|
'testcfx'])),
|
|
(("", "--pkgdir",), dict(dest="pkgdir",
|
|
help=("package dir containing "
|
|
"package.json; default is "
|
|
"current directory"),
|
|
metavar=None,
|
|
default=None,
|
|
cmds=['run', 'xpi', 'test'])),
|
|
(("", "--static-args",), dict(dest="static_args",
|
|
help="extra harness options as JSON",
|
|
type="json",
|
|
metavar=None,
|
|
default="{}",
|
|
cmds=['run', 'xpi'])),
|
|
(("", "--parseable",), dict(dest="parseable",
|
|
help="display test output in a parseable format",
|
|
action="store_true",
|
|
default=False,
|
|
cmds=['test', 'testex', 'testpkgs',
|
|
'testall'])),
|
|
]
|
|
),
|
|
|
|
("Experimental Command-Specific Options", [
|
|
(("-a", "--app",), dict(dest="app",
|
|
help=("app to run: firefox (default), fennec, "
|
|
"fennec-on-device, xulrunner or "
|
|
"thunderbird"),
|
|
metavar=None,
|
|
type="choice",
|
|
choices=["firefox", "fennec",
|
|
"fennec-on-device", "thunderbird",
|
|
"xulrunner"],
|
|
default="firefox",
|
|
cmds=['test', 'run', 'testex', 'testpkgs',
|
|
'testall'])),
|
|
(("", "--no-run",), dict(dest="no_run",
|
|
help=("Instead of launching the "
|
|
"application, just show the command "
|
|
"for doing so. Use this to launch "
|
|
"the application in a debugger like "
|
|
"gdb."),
|
|
action="store_true",
|
|
default=False,
|
|
cmds=['run', 'test'])),
|
|
(("", "--no-strip-xpi",), dict(dest="no_strip_xpi",
|
|
help="retain unused modules in XPI",
|
|
action="store_true",
|
|
default=False,
|
|
cmds=['xpi'])),
|
|
(("", "--force-mobile",), dict(dest="enable_mobile",
|
|
help="Force compatibility with Firefox Mobile",
|
|
action="store_true",
|
|
default=False,
|
|
cmds=['run', 'test', 'xpi', 'testall'])),
|
|
(("", "--mobile-app",), dict(dest="mobile_app_name",
|
|
help=("Name of your Android application to "
|
|
"use. Possible values: 'firefox', "
|
|
"'firefox_beta', 'fennec_aurora', "
|
|
"'fennec' (for nightly)."),
|
|
metavar=None,
|
|
default=None,
|
|
cmds=['run', 'test', 'testall'])),
|
|
(("", "--harness-option",), dict(dest="extra_harness_option_args",
|
|
help=("Extra properties added to "
|
|
"harness-options.json"),
|
|
action="append",
|
|
metavar="KEY=VALUE",
|
|
default=[],
|
|
cmds=['xpi'])),
|
|
(("", "--stop-on-error",), dict(dest="stopOnError",
|
|
help="Stop running tests after the first failure",
|
|
action="store_true",
|
|
metavar=None,
|
|
default=False,
|
|
cmds=['test', 'testex', 'testpkgs'])),
|
|
(("", "--override-version",), dict(dest="override_version",
|
|
help="Pass in a version string to use in generated docs",
|
|
metavar=None,
|
|
default=False,
|
|
cmds=['sdocs'])),
|
|
]
|
|
),
|
|
|
|
("Internal Command-Specific Options", [
|
|
(("", "--addons",), dict(dest="addons",
|
|
help=("paths of addons to install, "
|
|
"comma-separated"),
|
|
metavar=None,
|
|
default=None,
|
|
cmds=['test', 'run', 'testex', 'testpkgs',
|
|
'testall'])),
|
|
(("", "--baseurl",), dict(dest="baseurl",
|
|
help=("root of static docs tree: "
|
|
"for example: 'http://me.com/the_docs/'"),
|
|
metavar=None,
|
|
default='',
|
|
cmds=['sdocs'])),
|
|
(("", "--test-runner-pkg",), dict(dest="test_runner_pkg",
|
|
help=("name of package "
|
|
"containing test runner "
|
|
"program (default is "
|
|
"test-harness)"),
|
|
default="addon-sdk",
|
|
cmds=['test', 'testex', 'testpkgs',
|
|
'testall'])),
|
|
# --keydir was removed in 1.0b5, but we keep it around in the options
|
|
# parser to make life easier for frontends like FlightDeck which
|
|
# might still pass it. It can go away once the frontends are updated.
|
|
(("", "--keydir",), dict(dest="keydir",
|
|
help=("obsolete, ignored"),
|
|
metavar=None,
|
|
default=None,
|
|
cmds=['test', 'run', 'xpi', 'testex',
|
|
'testpkgs', 'testall'])),
|
|
(("", "--e10s",), dict(dest="enable_e10s",
|
|
help="enable out-of-process Jetpacks",
|
|
action="store_true",
|
|
default=False,
|
|
cmds=['test', 'run', 'testex', 'testpkgs'])),
|
|
(("", "--logfile",), dict(dest="logfile",
|
|
help="log console output to file",
|
|
metavar=None,
|
|
default=None,
|
|
cmds=['run', 'test', 'testex', 'testpkgs'])),
|
|
# TODO: This should default to true once our memory debugging
|
|
# issues are resolved; see bug 592774.
|
|
(("", "--profile-memory",), dict(dest="profileMemory",
|
|
help=("profile memory usage "
|
|
"(default is false)"),
|
|
type="int",
|
|
action="store",
|
|
default=0,
|
|
cmds=['test', 'testex', 'testpkgs',
|
|
'testall'])),
|
|
]
|
|
),
|
|
)
|
|
|
|
def find_parent_package(cur_dir):
|
|
tail = True
|
|
while tail:
|
|
if os.path.exists(os.path.join(cur_dir, 'package.json')):
|
|
return cur_dir
|
|
cur_dir, tail = os.path.split(cur_dir)
|
|
return None
|
|
|
|
def check_json(option, opt, value):
|
|
# We return the parsed JSON here; see bug 610816 for background on why.
|
|
try:
|
|
return json.loads(value)
|
|
except ValueError:
|
|
raise optparse.OptionValueError("Option %s must be JSON." % opt)
|
|
|
|
class CfxOption(optparse.Option):
|
|
TYPES = optparse.Option.TYPES + ('json',)
|
|
TYPE_CHECKER = copy(optparse.Option.TYPE_CHECKER)
|
|
TYPE_CHECKER['json'] = check_json
|
|
|
|
def parse_args(arguments, global_options, usage, version, parser_groups,
|
|
defaults=None):
|
|
parser = optparse.OptionParser(usage=usage.strip(), option_class=CfxOption,
|
|
version=version)
|
|
|
|
def name_cmp(a, b):
|
|
# a[0] = name sequence
|
|
# a[0][0] = short name (possibly empty string)
|
|
# a[0][1] = long name
|
|
names = []
|
|
for seq in (a, b):
|
|
names.append(seq[0][0][1:] if seq[0][0] else seq[0][1][2:])
|
|
return cmp(*names)
|
|
|
|
global_options.sort(name_cmp)
|
|
for names, opts in global_options:
|
|
parser.add_option(*names, **opts)
|
|
|
|
for group_name, options in parser_groups:
|
|
group = optparse.OptionGroup(parser, group_name)
|
|
options.sort(name_cmp)
|
|
for names, opts in options:
|
|
if 'cmds' in opts:
|
|
cmds = opts['cmds']
|
|
del opts['cmds']
|
|
cmds.sort()
|
|
if not 'help' in opts:
|
|
opts['help'] = ""
|
|
opts['help'] += " (%s)" % ", ".join(cmds)
|
|
group.add_option(*names, **opts)
|
|
parser.add_option_group(group)
|
|
|
|
if defaults:
|
|
parser.set_defaults(**defaults)
|
|
|
|
(options, args) = parser.parse_args(args=arguments)
|
|
|
|
if not args:
|
|
parser.print_help()
|
|
parser.exit()
|
|
|
|
return (options, args)
|
|
|
|
# all tests emit progress messages to stderr, not stdout. (the mozrunner
|
|
# console output goes to stderr and is hard to change, and
|
|
# unittest.TextTestRunner prefers stderr, so we send everything else there
|
|
# too, to keep all the messages in order)
|
|
|
|
def test_all(env_root, defaults):
|
|
fail = False
|
|
|
|
starttime = time.time()
|
|
|
|
if not defaults['filter']:
|
|
print >>sys.stderr, "Testing cfx..."
|
|
sys.stderr.flush()
|
|
result = test_cfx(env_root, defaults['verbose'])
|
|
if result.failures or result.errors:
|
|
fail = True
|
|
|
|
if not fail or not defaults.get("stopOnError"):
|
|
print >>sys.stderr, "Testing all examples..."
|
|
sys.stderr.flush()
|
|
|
|
try:
|
|
test_all_examples(env_root, defaults)
|
|
except SystemExit, e:
|
|
fail = (e.code != 0) or fail
|
|
|
|
if not fail or not defaults.get("stopOnError"):
|
|
print >>sys.stderr, "Testing all unit-test addons..."
|
|
sys.stderr.flush()
|
|
|
|
try:
|
|
test_all_testaddons(env_root, defaults)
|
|
except SystemExit, e:
|
|
fail = (e.code != 0) or fail
|
|
|
|
if not fail or not defaults.get("stopOnError"):
|
|
print >>sys.stderr, "Testing all packages..."
|
|
sys.stderr.flush()
|
|
try:
|
|
test_all_packages(env_root, defaults)
|
|
except SystemExit, e:
|
|
fail = (e.code != 0) or fail
|
|
|
|
print >>sys.stderr, "Total time for all tests: %f seconds" % (time.time() - starttime)
|
|
|
|
if fail:
|
|
print >>sys.stderr, "Some tests were unsuccessful."
|
|
sys.exit(1)
|
|
print >>sys.stderr, "All tests were successful. Ship it!"
|
|
sys.exit(0)
|
|
|
|
def test_cfx(env_root, verbose):
|
|
import cuddlefish.tests
|
|
|
|
# tests write to stderr. flush everything before and after to avoid
|
|
# confusion later.
|
|
sys.stdout.flush(); sys.stderr.flush()
|
|
olddir = os.getcwd()
|
|
os.chdir(env_root)
|
|
retval = cuddlefish.tests.run(verbose)
|
|
os.chdir(olddir)
|
|
sys.stdout.flush(); sys.stderr.flush()
|
|
return retval
|
|
|
|
def test_all_testaddons(env_root, defaults):
|
|
addons_dir = os.path.join(env_root, "test", "addons")
|
|
addons = [dirname for dirname in os.listdir(addons_dir)
|
|
if os.path.isdir(os.path.join(addons_dir, dirname))]
|
|
addons.sort()
|
|
fail = False
|
|
for dirname in addons:
|
|
print >>sys.stderr, "Testing %s..." % dirname
|
|
sys.stderr.flush()
|
|
try:
|
|
run(arguments=["run",
|
|
"--pkgdir",
|
|
os.path.join(addons_dir, dirname)],
|
|
defaults=defaults,
|
|
env_root=env_root)
|
|
except SystemExit, e:
|
|
fail = (e.code != 0) or fail
|
|
if fail and defaults.get("stopOnError"):
|
|
break
|
|
|
|
if fail:
|
|
print >>sys.stderr, "Some test addons tests were unsuccessful."
|
|
sys.exit(-1)
|
|
|
|
def test_all_examples(env_root, defaults):
|
|
examples_dir = os.path.join(env_root, "examples")
|
|
examples = [dirname for dirname in os.listdir(examples_dir)
|
|
if os.path.isdir(os.path.join(examples_dir, dirname))]
|
|
examples.sort()
|
|
fail = False
|
|
for dirname in examples:
|
|
print >>sys.stderr, "Testing %s..." % dirname
|
|
sys.stderr.flush()
|
|
try:
|
|
run(arguments=["test",
|
|
"--pkgdir",
|
|
os.path.join(examples_dir, dirname)],
|
|
defaults=defaults,
|
|
env_root=env_root)
|
|
except SystemExit, e:
|
|
fail = (e.code != 0) or fail
|
|
if fail and defaults.get("stopOnError"):
|
|
break
|
|
|
|
if fail:
|
|
print >>sys.stderr, "Some examples tests were unsuccessful."
|
|
sys.exit(-1)
|
|
|
|
def test_all_packages(env_root, defaults):
|
|
packages_dir = os.path.join(env_root, "packages")
|
|
if os.path.isdir(packages_dir):
|
|
packages = [dirname for dirname in os.listdir(packages_dir)
|
|
if os.path.isdir(os.path.join(packages_dir, dirname))]
|
|
else:
|
|
packages = []
|
|
packages.append(env_root)
|
|
packages.sort()
|
|
print >>sys.stderr, "Testing all available packages: %s." % (", ".join(packages))
|
|
sys.stderr.flush()
|
|
fail = False
|
|
for dirname in packages:
|
|
print >>sys.stderr, "Testing %s..." % dirname
|
|
sys.stderr.flush()
|
|
try:
|
|
run(arguments=["test",
|
|
"--pkgdir",
|
|
os.path.join(packages_dir, dirname)],
|
|
defaults=defaults,
|
|
env_root=env_root)
|
|
except SystemExit, e:
|
|
fail = (e.code != 0) or fail
|
|
if fail and defaults.get('stopOnError'):
|
|
break
|
|
if fail:
|
|
print >>sys.stderr, "Some package tests were unsuccessful."
|
|
sys.exit(-1)
|
|
|
|
def get_config_args(name, env_root):
|
|
local_json = os.path.join(env_root, "local.json")
|
|
if not (os.path.exists(local_json) and
|
|
os.path.isfile(local_json)):
|
|
if name == "default":
|
|
return []
|
|
else:
|
|
print >>sys.stderr, "File does not exist: %s" % local_json
|
|
sys.exit(1)
|
|
local_json = packaging.load_json_file(local_json)
|
|
if 'configs' not in local_json:
|
|
print >>sys.stderr, "'configs' key not found in local.json."
|
|
sys.exit(1)
|
|
if name not in local_json.configs:
|
|
if name == "default":
|
|
return []
|
|
else:
|
|
print >>sys.stderr, "No config found for '%s'." % name
|
|
sys.exit(1)
|
|
config = local_json.configs[name]
|
|
if type(config) != list:
|
|
print >>sys.stderr, "Config for '%s' must be a list of strings." % name
|
|
sys.exit(1)
|
|
return config
|
|
|
|
def initializer(env_root, args, out=sys.stdout, err=sys.stderr):
|
|
from templates import PACKAGE_JSON, TEST_MAIN_JS
|
|
from preflight import create_jid
|
|
path = os.getcwd()
|
|
addon = os.path.basename(path)
|
|
# if more than two arguments
|
|
if len(args) > 2:
|
|
print >>err, 'Too many arguments.'
|
|
return {"result":1}
|
|
if len(args) == 2:
|
|
path = os.path.join(path,args[1])
|
|
try:
|
|
os.mkdir(path)
|
|
print >>out, '*', args[1], 'package directory created'
|
|
except OSError:
|
|
print >>out, '*', args[1], 'already exists, testing if directory is empty'
|
|
# avoid clobbering existing files, but we tolerate things like .git
|
|
existing = [fn for fn in os.listdir(path) if not fn.startswith(".")]
|
|
if existing:
|
|
print >>err, 'This command must be run in an empty directory.'
|
|
return {"result":1}
|
|
for d in ['lib','data','test','doc']:
|
|
os.mkdir(os.path.join(path,d))
|
|
print >>out, '*', d, 'directory created'
|
|
open(os.path.join(path,'README.md'),'w').write('')
|
|
print >>out, '* README.md written'
|
|
jid = create_jid()
|
|
print >>out, '* generated jID automatically:', jid
|
|
open(os.path.join(path,'package.json'),'w').write(PACKAGE_JSON % {'name':addon.lower(),
|
|
'fullName':addon,
|
|
'id':jid })
|
|
print >>out, '* package.json written'
|
|
open(os.path.join(path,'test','test-main.js'),'w').write(TEST_MAIN_JS)
|
|
print >>out, '* test/test-main.js written'
|
|
open(os.path.join(path,'lib','main.js'),'w').write('')
|
|
print >>out, '* lib/main.js written'
|
|
open(os.path.join(path,'doc','main.md'),'w').write('')
|
|
print >>out, '* doc/main.md written'
|
|
if len(args) == 1:
|
|
print >>out, '\nYour sample add-on is now ready.'
|
|
print >>out, 'Do "cfx test" to test it and "cfx run" to try it. Have fun!'
|
|
else:
|
|
print >>out, '\nYour sample add-on is now ready in the \'' + args[1] + '\' directory.'
|
|
print >>out, 'Change to that directory, then do "cfx test" to test it, \nand "cfx run" to try it. Have fun!'
|
|
return {"result":0, "jid":jid}
|
|
|
|
def buildJID(target_cfg):
|
|
if "id" in target_cfg:
|
|
jid = target_cfg["id"]
|
|
else:
|
|
import uuid
|
|
jid = str(uuid.uuid4())
|
|
if not ("@" in jid or jid.startswith("{")):
|
|
jid = jid + "@jetpack"
|
|
return jid
|
|
|
|
def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None,
|
|
defaults=None, env_root=os.environ.get('CUDDLEFISH_ROOT'),
|
|
stdout=sys.stdout):
|
|
versions = get_versions()
|
|
sdk_version = versions["version"]
|
|
display_version = "Add-on SDK %s (%s)" % (sdk_version, versions["full"])
|
|
parser_kwargs = dict(arguments=arguments,
|
|
global_options=global_options,
|
|
parser_groups=parser_groups,
|
|
usage=usage,
|
|
version=display_version,
|
|
defaults=defaults)
|
|
|
|
(options, args) = parse_args(**parser_kwargs)
|
|
|
|
config_args = get_config_args(options.config, env_root);
|
|
|
|
# reparse configs with arguments from local.json
|
|
if config_args:
|
|
parser_kwargs['arguments'] += config_args
|
|
(options, args) = parse_args(**parser_kwargs)
|
|
|
|
command = args[0]
|
|
|
|
if command == "init":
|
|
initializer(env_root, args)
|
|
return
|
|
if command == "testpkgs":
|
|
test_all_packages(env_root, defaults=options.__dict__)
|
|
return
|
|
elif command == "testaddons":
|
|
test_all_testaddons(env_root, defaults=options.__dict__)
|
|
return
|
|
elif command == "testex":
|
|
test_all_examples(env_root, defaults=options.__dict__)
|
|
return
|
|
elif command == "testall":
|
|
test_all(env_root, defaults=options.__dict__)
|
|
return
|
|
elif command == "testcfx":
|
|
if options.filter:
|
|
print >>sys.stderr, "The filter option is not valid with the testcfx command"
|
|
return
|
|
test_cfx(env_root, options.verbose)
|
|
return
|
|
elif command == "docs":
|
|
from cuddlefish.docs import generate
|
|
if len(args) > 1:
|
|
docs_home = generate.generate_named_file(env_root, filename_and_path=args[1])
|
|
else:
|
|
docs_home = generate.generate_local_docs(env_root)
|
|
webbrowser.open(docs_home)
|
|
return
|
|
elif command == "sdocs":
|
|
from cuddlefish.docs import generate
|
|
filename=""
|
|
if options.override_version:
|
|
filename = generate.generate_static_docs(env_root, override_version=options.override_version)
|
|
else:
|
|
filename = generate.generate_static_docs(env_root)
|
|
print >>stdout, "Wrote %s." % filename
|
|
return
|
|
elif command not in ["xpi", "test", "run"]:
|
|
print >>sys.stderr, "Unknown command: %s" % command
|
|
print >>sys.stderr, "Try using '--help' for assistance."
|
|
sys.exit(1)
|
|
|
|
target_cfg_json = None
|
|
if not target_cfg:
|
|
if not options.pkgdir:
|
|
options.pkgdir = find_parent_package(os.getcwd())
|
|
if not options.pkgdir:
|
|
print >>sys.stderr, ("cannot find 'package.json' in the"
|
|
" current directory or any parent.")
|
|
sys.exit(1)
|
|
else:
|
|
options.pkgdir = os.path.abspath(options.pkgdir)
|
|
if not os.path.exists(os.path.join(options.pkgdir, 'package.json')):
|
|
print >>sys.stderr, ("cannot find 'package.json' in"
|
|
" %s." % options.pkgdir)
|
|
sys.exit(1)
|
|
|
|
target_cfg_json = os.path.join(options.pkgdir, 'package.json')
|
|
target_cfg = packaging.get_config_in_dir(options.pkgdir)
|
|
|
|
# At this point, we're either building an XPI or running Jetpack code in
|
|
# a Mozilla application (which includes running tests).
|
|
|
|
use_main = False
|
|
inherited_options = ['verbose', 'enable_e10s']
|
|
enforce_timeouts = False
|
|
|
|
if command == "xpi":
|
|
use_main = True
|
|
elif command == "test":
|
|
if 'tests' not in target_cfg:
|
|
target_cfg['tests'] = []
|
|
inherited_options.extend(['iterations', 'filter', 'profileMemory',
|
|
'stopOnError', 'parseable'])
|
|
enforce_timeouts = True
|
|
elif command == "run":
|
|
use_main = True
|
|
else:
|
|
assert 0, "shouldn't get here"
|
|
|
|
if use_main and 'main' not in target_cfg:
|
|
# If the user supplies a template dir, then the main
|
|
# program may be contained in the template.
|
|
if not options.templatedir:
|
|
print >>sys.stderr, "package.json does not have a 'main' entry."
|
|
sys.exit(1)
|
|
|
|
if not pkg_cfg:
|
|
pkg_cfg = packaging.build_config(env_root, target_cfg, options.packagepath)
|
|
|
|
target = target_cfg.name
|
|
|
|
# TODO: Consider keeping a cache of dynamic UUIDs, based
|
|
# on absolute filesystem pathname, in the root directory
|
|
# or something.
|
|
if command in ('xpi', 'run'):
|
|
from cuddlefish.preflight import preflight_config
|
|
if target_cfg_json:
|
|
config_was_ok, modified = preflight_config(target_cfg,
|
|
target_cfg_json)
|
|
if not config_was_ok:
|
|
if modified:
|
|
# we need to re-read package.json . The safest approach
|
|
# is to re-run the "cfx xpi"/"cfx run" command.
|
|
print >>sys.stderr, ("package.json modified: please re-run"
|
|
" 'cfx %s'" % command)
|
|
else:
|
|
print >>sys.stderr, ("package.json needs modification:"
|
|
" please update it and then re-run"
|
|
" 'cfx %s'" % command)
|
|
sys.exit(1)
|
|
# if we make it this far, we have a JID
|
|
else:
|
|
assert command == "test"
|
|
|
|
jid = buildJID(target_cfg)
|
|
|
|
targets = [target]
|
|
if command == "test":
|
|
targets.append(options.test_runner_pkg)
|
|
|
|
extra_packages = []
|
|
if options.extra_packages:
|
|
extra_packages = options.extra_packages.split(",")
|
|
if extra_packages:
|
|
targets.extend(extra_packages)
|
|
target_cfg.extra_dependencies = extra_packages
|
|
|
|
deps = packaging.get_deps_for_targets(pkg_cfg, targets)
|
|
|
|
from cuddlefish.manifest import build_manifest, ModuleNotFoundError, \
|
|
BadChromeMarkerError
|
|
# Figure out what loader files should be scanned. This is normally
|
|
# computed inside packaging.generate_build_for_target(), by the first
|
|
# dependent package that defines a "loader" property in its package.json.
|
|
# This property is interpreted as a filename relative to the top of that
|
|
# file, and stored as a path in build.loader . generate_build_for_target()
|
|
# cannot be called yet (it needs the list of used_deps that
|
|
# build_manifest() computes, but build_manifest() needs the list of
|
|
# loader files that it computes). We could duplicate or factor out this
|
|
# build.loader logic, but that would be messy, so instead we hard-code
|
|
# the choice of loader for manifest-generation purposes. In practice,
|
|
# this means that alternative loaders probably won't work with
|
|
# --strip-xpi.
|
|
assert packaging.DEFAULT_LOADER == "addon-sdk"
|
|
assert pkg_cfg.packages["addon-sdk"].loader == "lib/sdk/loader/cuddlefish.js"
|
|
cuddlefish_js_path = os.path.join(pkg_cfg.packages["addon-sdk"].root_dir,
|
|
"lib", "sdk", "loader", "cuddlefish.js")
|
|
loader_modules = [("addon-sdk", "lib", "sdk/loader/cuddlefish", cuddlefish_js_path)]
|
|
scan_tests = command == "test"
|
|
test_filter_re = None
|
|
if scan_tests and options.filter:
|
|
test_filter_re = options.filter
|
|
if ":" in options.filter:
|
|
test_filter_re = options.filter.split(":")[0]
|
|
try:
|
|
manifest = build_manifest(target_cfg, pkg_cfg, deps,
|
|
scan_tests, test_filter_re,
|
|
loader_modules)
|
|
except ModuleNotFoundError, e:
|
|
print str(e)
|
|
sys.exit(1)
|
|
except BadChromeMarkerError, e:
|
|
# An error had already been displayed on stderr in manifest code
|
|
sys.exit(1)
|
|
used_deps = manifest.get_used_packages()
|
|
if command == "test":
|
|
# The test runner doesn't appear to link against any actual packages,
|
|
# because it loads everything at runtime (invisible to the linker).
|
|
# If we believe that, we won't set up URI mappings for anything, and
|
|
# tests won't be able to run.
|
|
used_deps = deps
|
|
for xp in extra_packages:
|
|
if xp not in used_deps:
|
|
used_deps.append(xp)
|
|
|
|
build = packaging.generate_build_for_target(
|
|
pkg_cfg, target, used_deps,
|
|
include_dep_tests=options.dep_tests,
|
|
is_running_tests=(command == "test")
|
|
)
|
|
|
|
harness_options = {
|
|
'jetpackID': jid,
|
|
'staticArgs': options.static_args,
|
|
'name': target,
|
|
}
|
|
|
|
harness_options.update(build)
|
|
|
|
extra_environment = {}
|
|
if command == "test":
|
|
# This should be contained in the test runner package.
|
|
# maybe just do: target_cfg.main = 'test-harness/run-tests'
|
|
harness_options['main'] = 'sdk/test/runner'
|
|
harness_options['mainPath'] = manifest.get_manifest_entry("addon-sdk", "lib", "sdk/test/runner").get_path()
|
|
else:
|
|
harness_options['main'] = target_cfg.get('main')
|
|
harness_options['mainPath'] = manifest.top_path
|
|
extra_environment["CFX_COMMAND"] = command
|
|
|
|
for option in inherited_options:
|
|
harness_options[option] = getattr(options, option)
|
|
|
|
harness_options['metadata'] = packaging.get_metadata(pkg_cfg, used_deps)
|
|
|
|
harness_options['sdkVersion'] = sdk_version
|
|
|
|
packaging.call_plugins(pkg_cfg, used_deps)
|
|
|
|
retval = 0
|
|
|
|
if options.templatedir:
|
|
app_extension_dir = os.path.abspath(options.templatedir)
|
|
else:
|
|
mydir = os.path.dirname(os.path.abspath(__file__))
|
|
app_extension_dir = os.path.join(mydir, "../../app-extension")
|
|
|
|
|
|
if target_cfg.get('preferences'):
|
|
harness_options['preferences'] = target_cfg.get('preferences')
|
|
|
|
harness_options['manifest'] = manifest.get_harness_options_manifest()
|
|
harness_options['allTestModules'] = manifest.get_all_test_modules()
|
|
if len(harness_options['allTestModules']) == 0 and command == "test":
|
|
sys.exit(0)
|
|
|
|
from cuddlefish.rdf import gen_manifest, RDFUpdate
|
|
|
|
manifest_rdf = gen_manifest(template_root_dir=app_extension_dir,
|
|
target_cfg=target_cfg,
|
|
jid=jid,
|
|
update_url=options.update_url,
|
|
bootstrap=True,
|
|
enable_mobile=options.enable_mobile)
|
|
|
|
if command == "xpi" and options.update_link:
|
|
if not options.update_link.startswith("https"):
|
|
raise optparse.OptionValueError("--update-link must start with 'https': %s" % options.update_link)
|
|
rdf_name = UPDATE_RDF_FILENAME % target_cfg.name
|
|
print >>stdout, "Exporting update description to %s." % rdf_name
|
|
update = RDFUpdate()
|
|
update.add(manifest_rdf, options.update_link)
|
|
open(rdf_name, "w").write(str(update))
|
|
|
|
# ask the manifest what files were used, so we can construct an XPI
|
|
# without the rest. This will include the loader (and everything it
|
|
# uses) because of the "loader_modules" starting points we passed to
|
|
# build_manifest earlier
|
|
used_files = None
|
|
if command == "xpi":
|
|
used_files = set(manifest.get_used_files())
|
|
|
|
if options.no_strip_xpi:
|
|
used_files = None # disables the filter, includes all files
|
|
|
|
if command == 'xpi':
|
|
from cuddlefish.xpi import build_xpi
|
|
extra_harness_options = {}
|
|
for kv in options.extra_harness_option_args:
|
|
key,value = kv.split("=", 1)
|
|
extra_harness_options[key] = value
|
|
xpi_path = XPI_FILENAME % target_cfg.name
|
|
print >>stdout, "Exporting extension to %s." % xpi_path
|
|
build_xpi(template_root_dir=app_extension_dir,
|
|
manifest=manifest_rdf,
|
|
xpi_path=xpi_path,
|
|
harness_options=harness_options,
|
|
limit_to=used_files,
|
|
extra_harness_options=extra_harness_options)
|
|
else:
|
|
from cuddlefish.runner import run_app
|
|
|
|
if options.profiledir:
|
|
options.profiledir = os.path.expanduser(options.profiledir)
|
|
options.profiledir = os.path.abspath(options.profiledir)
|
|
|
|
if options.addons is not None:
|
|
options.addons = options.addons.split(",")
|
|
|
|
try:
|
|
retval = run_app(harness_root_dir=app_extension_dir,
|
|
manifest_rdf=manifest_rdf,
|
|
harness_options=harness_options,
|
|
app_type=options.app,
|
|
binary=options.binary,
|
|
profiledir=options.profiledir,
|
|
verbose=options.verbose,
|
|
enforce_timeouts=enforce_timeouts,
|
|
logfile=options.logfile,
|
|
addons=options.addons,
|
|
args=options.cmdargs,
|
|
extra_environment=extra_environment,
|
|
norun=options.no_run,
|
|
used_files=used_files,
|
|
enable_mobile=options.enable_mobile,
|
|
mobile_app_name=options.mobile_app_name)
|
|
except ValueError, e:
|
|
print ""
|
|
print "A given cfx option has an inappropriate value:"
|
|
print >>sys.stderr, " " + " \n ".join(str(e).split("\n"))
|
|
retval = -1
|
|
except Exception, e:
|
|
if str(e).startswith(MOZRUNNER_BIN_NOT_FOUND):
|
|
print >>sys.stderr, MOZRUNNER_BIN_NOT_FOUND_HELP.strip()
|
|
retval = -1
|
|
else:
|
|
raise
|
|
sys.exit(retval)
|