123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937 |
- # 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:
- init - create a sample addon in an empty directory
- test - run tests
- run - run program
- xpi - generate an xpi
- Internal Commands:
- 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=['run', 'test', 'testex', 'testpkgs',
- 'testaddons', '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'])),
- (("-o", "--overload-modules",), dict(dest="overload_modules",
- help=("Overload JS modules integrated into"
- " Firefox with the one from your SDK"
- " repository"),
- action="store_true",
- default=False,
- cmds=['run', 'test', 'testex', 'testpkgs',
- 'testall'])),
- (("", "--strip-sdk",), dict(dest="bundle_sdk",
- help=("Do not ship SDK modules in the xpi"),
- action="store_false",
- default=False,
- cmds=['run', 'test', 'testex', 'testpkgs',
- 'testall', 'xpi'])),
- (("", "--force-use-bundled-sdk",), dict(dest="force_use_bundled_sdk",
- help=("When --strip-sdk isn't passed, "
- "force using sdk modules shipped in "
- "the xpi instead of firefox ones"),
- action="store_true",
- default=False,
- cmds=['run', 'test', 'testex', 'testpkgs',
- 'testall', 'xpi'])),
- (("", "--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'])),
- (("", "--check-memory",), dict(dest="check_memory",
- help="attempts to detect leaked compartments after a test run",
- action="store_true",
- default=False,
- cmds=['test', 'testpkgs', 'testaddons',
- 'testall'])),
- (("", "--output-file",), dict(dest="output_file",
- help="Where to put the finished .xpi",
- default=None,
- cmds=['xpi'])),
- (("", "--manifest-overload",), dict(dest="manifest_overload",
- help="JSON file to overload package.json properties",
- default=None,
- cmds=['xpi'])),
- ]
- ),
- ("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'])),
- (("", "--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(),
- 'title':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 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)
- if options.manifest_overload:
- for k, v in packaging.load_json_file(options.manifest_overload).items():
- target_cfg[k] = v
- # 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', 'parseable', 'check_memory']
- 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'])
- 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)
- # When cfx is run from sdk root directory, we will strip sdk modules and
- # override them with local modules.
- # So that integration tools will continue to work and use local modules
- if os.getcwd() == env_root:
- options.bundle_sdk = True
- options.force_use_bundled_sdk = False
- options.overload_modules = True
- 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'] = 'sdk/test/runner'
- 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)
- elif os.path.exists(os.path.join(options.pkgdir, "app-extension")):
- app_extension_dir = os.path.join(options.pkgdir, "app-extension")
- 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')
- # Do not add entries for SDK modules
- harness_options['manifest'] = manifest.get_harness_options_manifest(False)
- # Gives an hint to tell if sdk modules are bundled or not
- harness_options['is-sdk-bundled'] = options.bundle_sdk
- if options.force_use_bundled_sdk:
- if not options.bundle_sdk:
- print >>sys.stderr, ("--force-use-bundled-sdk and --strip-sdk "
- "can't be used at the same time.")
- sys.exit(1)
- if options.overload_modules:
- print >>sys.stderr, ("--force-use-bundled-sdk and --overload-modules "
- "can't be used at the same time.")
- sys.exit(1)
- # Pass a flag in order to force using sdk modules shipped in the xpi
- harness_options['force-use-bundled-sdk'] = True
- # Pass the list of absolute path for all test modules
- if command == "test":
- harness_options['allTestModules'] = manifest.get_all_test_modules()
- if len(harness_options['allTestModules']) == 0:
- 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(options.bundle_sdk))
- if options.no_strip_xpi:
- used_files = None # disables the filter, includes all files
- if command == 'xpi':
- from cuddlefish.xpi import build_xpi
- # Generate extra options
- extra_harness_options = {}
- for kv in options.extra_harness_option_args:
- key,value = kv.split("=", 1)
- extra_harness_options[key] = value
- # Generate xpi filepath
- if options.output_file:
- xpi_path = options.output_file
- else:
- 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,
- bundle_sdk=True,
- pkgdir=options.pkgdir)
- 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,
- parseable=options.parseable,
- 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,
- env_root=env_root,
- is_running_tests=(command == "test"),
- overload_modules=options.overload_modules,
- bundle_sdk=options.bundle_sdk,
- pkgdir=options.pkgdir)
- 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)
|