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 == "testex": test_all_examples(env_root, defaults=options.__dict__) return elif command == "testall": test_all(env_root, defaults=options.__dict__) return elif command == "testcfx": 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=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 = generate.generate_static_docs(env_root) print >>stdout, "Wrote %s." % filename return 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']) enforce_timeouts = True elif command == "run": use_main = True else: print >>sys.stderr, "Unknown command: %s" % command print >>sys.stderr, "Try using '--help' for assistance." sys.exit(1) 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 # 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 == "api-utils" assert pkg_cfg.packages["api-utils"].loader == "lib/cuddlefish.js" cuddlefish_js_path = os.path.join(pkg_cfg.packages["api-utils"].root_dir, "lib", "cuddlefish.js") loader_modules = [("api-utils", "lib", "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)
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', 'abort_on_missing' ] 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, abort_on_missing=options.abort_on_missing) except ModuleNotFoundError, e: print str(e) sys.exit(1)
def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None, defaults=None, env_root=os.environ.get('CUDDLEFISH_ROOT')): parser_kwargs = dict(arguments=arguments, parser_options=parser_options, parser_groups=parser_groups, usage=usage, 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 == "develop": run_development_mode(env_root, defaults=options.__dict__) return if command == "testpkgs": test_all_packages(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": test_cfx(env_root, options.verbose) return elif command == "docs": import subprocess import time import cuddlefish.server print "One moment." popen = subprocess.Popen( [sys.executable, cuddlefish.server.__file__, 'daemonic']) # TODO: See if there's actually a way to block on # a particular event occurring, rather than this # relatively arbitrary/generous amount. time.sleep(cuddlefish.server.IDLE_WEBPAGE_TIMEOUT * 2) return elif command == "sdocs": import cuddlefish.server # TODO: Allow user to change this filename via cmd line. filename = 'jetpack-sdk-docs.tgz' cuddlefish.server.generate_static_docs(env_root, filename) print "Wrote %s." % filename return 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) use_main = False if command == "xpcom": if 'xpcom' not in target_cfg: print >> sys.stderr, "package.json does not have a 'xpcom' entry." sys.exit(1) if not (options.moz_srcdir and options.moz_objdir): print >> sys.stderr, "srcdir and objdir not specified." sys.exit(1) options.moz_srcdir = os.path.expanduser(options.moz_srcdir) options.moz_objdir = os.path.expanduser(options.moz_objdir) xpcom = target_cfg.xpcom from cuddlefish.xpcom import build_xpcom_components if 'typelibs' in xpcom: xpt_output_dir = packaging.resolve_dir(target_cfg, xpcom.typelibs) else: xpt_output_dir = None build_xpcom_components( comp_src_dir=packaging.resolve_dir(target_cfg, xpcom.src), moz_srcdir=options.moz_srcdir, moz_objdir=options.moz_objdir, base_output_dir=packaging.resolve_dir(target_cfg, xpcom.dest), xpt_output_dir=xpt_output_dir, module_name=xpcom.module) sys.exit(0) elif command == "xpi": use_main = True elif command == "test": if 'tests' not in target_cfg: target_cfg['tests'] = [] elif command == "run": use_main = True else: print >> sys.stderr, "Unknown command: %s" % command print >> sys.stderr, "Try using '--help' for assistance." sys.exit(1) 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) 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, keydir=options.keydir, err_if_privkey_not_found=False) 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 jid = target_cfg["id"] assert not jid.endswith("@jetpack") unique_prefix = '%s-' % jid # used for resource: URLs # the harness_guid is used for an XPCOM class ID. We use the # JetpackID for the add-on ID and the XPCOM contract ID. import uuid harness_guid = str(uuid.uuid4()) else: if options.use_server: harness_guid = '2974c5b5-b671-46f8-a4bb-63c6eca6261b' else: harness_guid = '6724fc1b-3ec4-40e2-8583-8061088b3185' unique_prefix = '%s-' % target jid = harness_guid assert not jid.endswith("@jetpack") bundle_id = jid + "@jetpack" # the resource: URLs prefix is treated too much like a DNS hostname unique_prefix = unique_prefix.lower() assert "@" not in unique_prefix assert "." not in unique_prefix timeout = None targets = [target] if not use_main: timeout = TEST_RUN_TIMEOUT targets.append(options.test_runner_pkg) if options.extra_packages: targets.extend(options.extra_packages.split(",")) deps = packaging.get_deps_for_targets(pkg_cfg, targets) build = packaging.generate_build_for_target( pkg_cfg, target, deps, prefix=unique_prefix, # used to create resource: URLs include_dep_tests=options.dep_tests) if 'resources' in build: resources = build.resources for name in resources: resources[name] = os.path.abspath(resources[name]) dep_xpt_dirs = [] for dep in deps: dep_cfg = pkg_cfg.packages[dep] if 'xpcom' in dep_cfg and 'typelibs' in dep_cfg.xpcom: abspath = packaging.resolve_dir(dep_cfg, dep_cfg.xpcom.typelibs) dep_xpt_dirs.append(abspath) xpts = get_xpts(dep_xpt_dirs) harness_contract_id = ('@mozilla.org/harness-service;1?id=%s' % jid) harness_options = { 'bootstrap': { 'contractID': harness_contract_id, 'classID': '{%s}' % harness_guid }, 'jetpackID': jid, 'bundleID': bundle_id, } harness_options.update(build) inherited_options = ['verbose'] if use_main: harness_options['main'] = target_cfg.get('main') else: harness_options['main'] = "run-tests" inherited_options.extend(['iterations', 'filter', 'profileMemory']) for option in inherited_options: harness_options[option] = getattr(options, option) harness_options['metadata'] = packaging.get_metadata(pkg_cfg, deps) packaging.call_plugins(pkg_cfg, deps) retval = 0 if options.templatedir: app_extension_dir = os.path.abspath(options.templatedir) else: mydir = os.path.dirname(os.path.abspath(__file__)) if sys.platform == "darwin": # If we're on OS X, at least point into the XULRunner # app dir so we run as a proper app if using XULRunner. app_extension_dir = os.path.join(mydir, "Test App.app", "Contents", "Resources") else: app_extension_dir = os.path.join(mydir, "app-extension") if command == 'xpi': from cuddlefish.xpi import build_xpi from cuddlefish.rdf import gen_manifest, RDFUpdate manifest = gen_manifest(template_root_dir=app_extension_dir, target_cfg=target_cfg, bundle_id=bundle_id, update_url=options.update_url, bootstrap=True) if options.update_link: rdf_name = UPDATE_RDF_FILENAME % target_cfg.name print "Exporting update description to %s." % rdf_name update = RDFUpdate() update.add(manifest, options.update_link) open(rdf_name, "w").write(str(update)) xpi_name = XPI_FILENAME % target_cfg.name print "Exporting extension to %s." % xpi_name build_xpi(template_root_dir=app_extension_dir, manifest=manifest, xpi_name=xpi_name, harness_options=harness_options, xpts=xpts) else: if options.use_server: from cuddlefish.server import run_app 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, harness_options=harness_options, xpts=xpts, app_type=options.app, binary=options.binary, profiledir=options.profiledir, verbose=options.verbose, timeout=timeout, logfile=options.logfile, addons=options.addons) except Exception, e: if e.message.startswith(MOZRUNNER_BIN_NOT_FOUND): print >> sys.stderr, MOZRUNNER_BIN_NOT_FOUND_HELP.strip() retval = -1 else: raise
def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None, defaults=None, env_root=os.environ.get('CUDDLEFISH_ROOT')): parser_kwargs = dict(arguments=arguments, global_options=global_options, parser_groups=parser_groups, usage=usage, 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 == "develop": run_development_mode(env_root, defaults=options.__dict__) return if command == "testpkgs": test_all_packages(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": test_cfx(env_root, options.verbose) return elif command == "docs": import subprocess import time import cuddlefish.server print "One moment." popen = subprocess.Popen([sys.executable, cuddlefish.server.__file__, 'daemonic']) # TODO: See if there's actually a way to block on # a particular event occurring, rather than this # relatively arbitrary/generous amount. time.sleep(cuddlefish.server.IDLE_WEBPAGE_TIMEOUT * 2) return elif command == "sdocs": import cuddlefish.server # TODO: Allow user to change this filename via cmd line. filename = 'addon-sdk-docs.tgz' cuddlefish.server.generate_static_docs(env_root, filename, options.baseurl) print "Wrote %s." % filename return 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 timeout = None inherited_options = ['verbose', 'enable_e10s'] if command == "xpi": use_main = True elif command == "test": if 'tests' not in target_cfg: target_cfg['tests'] = [] timeout = TEST_RUN_TIMEOUT inherited_options.extend(['iterations', 'filter', 'profileMemory']) elif command == "run": use_main = True else: print >>sys.stderr, "Unknown command: %s" % command print >>sys.stderr, "Try using '--help' for assistance." sys.exit(1) 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 # the harness_guid is used for an XPCOM class ID. We use the # JetpackID for the add-on ID and the XPCOM contract ID. if "harnessClassID" in target_cfg: # For the sake of non-bootstrapped extensions, we allow to specify the # classID of harness' XPCOM component in package.json. This makes it # possible to register the component using a static chrome.manifest file harness_guid = target_cfg["harnessClassID"] else: import uuid harness_guid = str(uuid.uuid4()) # 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" if "id" in target_cfg: jid = target_cfg["id"] assert not jid.endswith("@jetpack") unique_prefix = '%s-' % jid # used for resource: URLs else: # The Jetpack ID is not required for cfx test, in which case we have to # make one up based on the GUID. if options.use_server: # The harness' contractID (hence also the jid and the harness_guid) # need to be static in the "development mode", so that bootstrap.js # can unload the previous version of the package being developed. harness_guid = '2974c5b5-b671-46f8-a4bb-63c6eca6261b' unique_prefix = '%s-' % target jid = harness_guid assert not jid.endswith("@jetpack") if ( jid.startswith("jid0-") or jid.startswith("jid1-") or jid.startswith("anonid0-") ): bundle_id = jid + "@jetpack" # Don't append "@jetpack" to old-style IDs, as they should be exactly # as specified by the addon author so AMO and Firefox continue to treat # their addon bundles as representing the same addon (and also because # they may already have an @ sign in them, and there can be only one). else: bundle_id = jid # the resource: URL's prefix is treated too much like a DNS hostname unique_prefix = unique_prefix.lower() unique_prefix = unique_prefix.replace("@", "-at-") unique_prefix = unique_prefix.replace(".", "-dot-") targets = [target] if command == "test": targets.append(options.test_runner_pkg) if options.extra_packages: targets.extend(options.extra_packages.split(",")) deps = packaging.get_deps_for_targets(pkg_cfg, targets) build = packaging.generate_build_for_target( pkg_cfg, target, deps, prefix=unique_prefix, # used to create resource: URLs include_dep_tests=options.dep_tests ) if 'resources' in build: resources = build.resources for name in resources: resources[name] = os.path.abspath(resources[name]) harness_contract_id = ('@mozilla.org/harness-service;1?id=%s' % jid) harness_options = { 'bootstrap': { 'contractID': harness_contract_id, 'classID': '{%s}' % harness_guid }, 'jetpackID': jid, 'bundleID': bundle_id, 'staticArgs': options.static_args, 'name': target, } harness_options.update(build) if command == "test": # This should be contained in the test runner package. harness_options['main'] = 'run-tests' else: harness_options['main'] = target_cfg.get('main') for option in inherited_options: harness_options[option] = getattr(options, option) harness_options['metadata'] = packaging.get_metadata(pkg_cfg, deps) sdk_version = get_version(env_root) harness_options['sdkVersion'] = sdk_version packaging.call_plugins(pkg_cfg, deps) retval = 0 if options.templatedir: app_extension_dir = os.path.abspath(options.templatedir) else: mydir = os.path.dirname(os.path.abspath(__file__)) if sys.platform == "darwin": # If we're on OS X, at least point into the XULRunner # app dir so we run as a proper app if using XULRunner. app_extension_dir = os.path.join(mydir, "Test App.app", "Contents", "Resources") else: app_extension_dir = os.path.join(mydir, "app-extension") from cuddlefish.manifest import build_manifest uri_prefix = "resource://%s" % unique_prefix include_tests = False #bool(command=="test") manifest = build_manifest(target_cfg, pkg_cfg, deps, uri_prefix, include_tests) harness_options['manifest'] = manifest.get_harness_options_manifest(uri_prefix) if command == 'xpi': from cuddlefish.xpi import build_xpi from cuddlefish.rdf import gen_manifest, RDFUpdate manifest_rdf = gen_manifest(template_root_dir=app_extension_dir, target_cfg=target_cfg, bundle_id=bundle_id, update_url=options.update_url, bootstrap=True) if options.update_link: rdf_name = UPDATE_RDF_FILENAME % target_cfg.name print "Exporting update description to %s." % rdf_name update = RDFUpdate() update.add(manifest_rdf, options.update_link) open(rdf_name, "w").write(str(update)) xpi_name = XPI_FILENAME % target_cfg.name print "Exporting extension to %s." % xpi_name build_xpi(template_root_dir=app_extension_dir, manifest=manifest_rdf, xpi_name=xpi_name, harness_options=harness_options) else: if options.use_server: from cuddlefish.server import run_app 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, harness_options=harness_options, app_type=options.app, binary=options.binary, profiledir=options.profiledir, verbose=options.verbose, timeout=timeout, logfile=options.logfile, addons=options.addons, args=options.cmdargs, norun=options.no_run) except Exception, e: if str(e).startswith(MOZRUNNER_BIN_NOT_FOUND): print >>sys.stderr, MOZRUNNER_BIN_NOT_FOUND_HELP.strip() retval = -1 else: raise
def test_no_name(self): basedir = self.make_basedir() fn = os.path.join(basedir, "package.json") keydir = os.path.join(basedir, "keys") # empty config is not ok: need id (name is automatically supplied) config_orig = "{}" self.write(config_orig) out = StringIO() cfg = self.get_cfg() config_was_ok, modified = preflight.preflight_config(cfg, fn, stderr=out, keydir=keydir) self.failUnlessEqual(config_was_ok, False) self.failUnlessEqual(modified, True) backup_fn = os.path.join(basedir, "package.json.backup") config_backup = open(backup_fn,"r").read() self.failUnlessEqual(config_backup, config_orig) config = json.loads(self.read()) self.failIf("name" in config) self.failUnless("id" in config) self.failUnlessEqual(out.getvalue().strip(), "No 'id' in package.json: creating a new keypair for you.") jid = str(config["id"]) keyfile = os.path.join(keydir, jid) fields, fieldnames = self.parse(open(keyfile).read()) self.failUnlessEqual(fieldnames[0], "private-key") privkey = fields["private-key"] self.failUnless(privkey.startswith("private-jid0-"), privkey) self.failUnlessEqual(fields["jid"], jid) self.failUnlessEqual(fields["name"], "pretend name") os.unlink(backup_fn) # just a name? we add the id config_orig = '{"name": "my-awesome-package"}' self.write(config_orig) out = StringIO() cfg = self.get_cfg() config_was_ok, modified = preflight.preflight_config(cfg, fn, stderr=out, keydir=keydir) self.failUnlessEqual(config_was_ok, False) self.failUnlessEqual(modified, True) backup_fn = os.path.join(basedir, "package.json.backup") config_backup = open(backup_fn,"r").read() self.failUnlessEqual(config_backup, config_orig) config = json.loads(self.read()) self.failUnlessEqual(config["name"], "my-awesome-package") self.failUnless("id" in config) self.failUnlessEqual(out.getvalue().strip(), "No 'id' in package.json: creating a new keypair for you.") jid = str(config["id"]) keyfile = os.path.join(keydir, jid) fields, fieldnames = self.parse(open(keyfile).read()) privkey = fields["private-key"] self.failUnless(privkey.startswith("private-jid0-"), privkey) self.failUnlessEqual(fields["jid"], jid) self.failUnlessEqual(fields["name"], "my-awesome-package") # name and valid id? great! ship it! config2 = '{"name": "my-awesome-package", "id": "%s"}' % jid self.write(config2) out = StringIO() cfg = self.get_cfg() config_was_ok, modified = preflight.preflight_config(cfg, fn, stderr=out, keydir=keydir) self.failUnlessEqual(config_was_ok, True) self.failUnlessEqual(modified, False) config2a = self.read() self.failUnlessEqual(config2a, config2) self.failUnlessEqual(out.getvalue().strip(), "") # name and invalid id? tell them to get a new one os.unlink(keyfile) self.write(config2) out = StringIO() cfg = self.get_cfg() config_was_ok, modified = preflight.preflight_config(cfg, fn, stderr=out, keydir=keydir) self.failUnlessEqual(config_was_ok, False) self.failUnlessEqual(modified, False) out = out.getvalue().strip() self.failUnless("Your package.json says our ID is" in out, out) self.failUnless("But I don't have a corresponding private key in" in out, out) self.failUnless("If you are the original developer" in out, out) self.failUnless("Otherwise, if you are a new developer" in out, out)
def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None, defaults=None, env_root=os.environ.get('CUDDLEFISH_ROOT')): parser_kwargs = dict(arguments=arguments, global_options=global_options, parser_groups=parser_groups, usage=usage, 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 == "develop": run_development_mode(env_root, defaults=options.__dict__) return if command == "testpkgs": test_all_packages(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": test_cfx(env_root, options.verbose) return elif command == "docs": import subprocess import time import cuddlefish.server print "One moment." popen = subprocess.Popen( [sys.executable, cuddlefish.server.__file__, 'daemonic']) # TODO: See if there's actually a way to block on # a particular event occurring, rather than this # relatively arbitrary/generous amount. time.sleep(cuddlefish.server.IDLE_WEBPAGE_TIMEOUT * 2) return elif command == "sdocs": import cuddlefish.server # TODO: Allow user to change this filename via cmd line. filename = 'addon-sdk-docs.tgz' cuddlefish.server.generate_static_docs(env_root, filename, options.baseurl) print "Wrote %s." % filename return 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 timeout = None inherited_options = ['verbose', 'enable_e10s'] if command == "xpi": use_main = True elif command == "test": if 'tests' not in target_cfg: target_cfg['tests'] = [] timeout = TEST_RUN_TIMEOUT inherited_options.extend(['iterations', 'filter', 'profileMemory']) elif command == "run": use_main = True else: print >> sys.stderr, "Unknown command: %s" % command print >> sys.stderr, "Try using '--help' for assistance." sys.exit(1) 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) target = target_cfg.name # the harness_guid is used for an XPCOM class ID. We use the # JetpackID for the add-on ID and the XPCOM contract ID. if "harnessClassID" in target_cfg: # For the sake of non-bootstrapped extensions, we allow to specify the # classID of harness' XPCOM component in package.json. This makes it # possible to register the component using a static chrome.manifest file harness_guid = target_cfg["harnessClassID"] else: import uuid harness_guid = str(uuid.uuid4()) # 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, keydir=options.keydir, err_if_privkey_not_found=False) 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" if "id" in target_cfg: jid = target_cfg["id"] assert not jid.endswith("@jetpack") unique_prefix = '%s-' % jid # used for resource: URLs else: # The Jetpack ID is not required for cfx test, in which case we have to # make one up based on the GUID. if options.use_server: # The harness' contractID (hence also the jid and the harness_guid) # need to be static in the "development mode", so that bootstrap.js # can unload the previous version of the package being developed. harness_guid = '2974c5b5-b671-46f8-a4bb-63c6eca6261b' unique_prefix = '%s-' % target jid = harness_guid assert not jid.endswith("@jetpack") if (jid.startswith("jid0-") or jid.startswith("anonid0-")): bundle_id = jid + "@jetpack" # Don't append "@jetpack" to old-style IDs, as they should be exactly # as specified by the addon author so AMO and Firefox continue to treat # their addon bundles as representing the same addon (and also because # they may already have an @ sign in them, and there can be only one). else: bundle_id = jid # the resource: URL's prefix is treated too much like a DNS hostname unique_prefix = unique_prefix.lower() unique_prefix = unique_prefix.replace("@", "-at-") unique_prefix = unique_prefix.replace(".", "-dot-") targets = [target] if command == "test": targets.append(options.test_runner_pkg) if options.extra_packages: targets.extend(options.extra_packages.split(",")) deps = packaging.get_deps_for_targets(pkg_cfg, targets) build = packaging.generate_build_for_target( pkg_cfg, target, deps, prefix=unique_prefix, # used to create resource: URLs include_dep_tests=options.dep_tests) if 'resources' in build: resources = build.resources for name in resources: resources[name] = os.path.abspath(resources[name]) harness_contract_id = ('@mozilla.org/harness-service;1?id=%s' % jid) harness_options = { 'bootstrap': { 'contractID': harness_contract_id, 'classID': '{%s}' % harness_guid }, 'jetpackID': jid, 'bundleID': bundle_id, 'staticArgs': options.static_args, 'name': target, } harness_options.update(build) if command == "test": # This should be contained in the test runner package. harness_options['main'] = 'run-tests' else: harness_options['main'] = target_cfg.get('main') for option in inherited_options: harness_options[option] = getattr(options, option) harness_options['metadata'] = packaging.get_metadata(pkg_cfg, deps) sdk_version = get_version(env_root) harness_options['sdkVersion'] = sdk_version packaging.call_plugins(pkg_cfg, deps) retval = 0 if options.templatedir: app_extension_dir = os.path.abspath(options.templatedir) else: mydir = os.path.dirname(os.path.abspath(__file__)) if sys.platform == "darwin": # If we're on OS X, at least point into the XULRunner # app dir so we run as a proper app if using XULRunner. app_extension_dir = os.path.join(mydir, "Test App.app", "Contents", "Resources") else: app_extension_dir = os.path.join(mydir, "app-extension") if command == 'xpi': from cuddlefish.xpi import build_xpi from cuddlefish.rdf import gen_manifest, RDFUpdate manifest = gen_manifest(template_root_dir=app_extension_dir, target_cfg=target_cfg, bundle_id=bundle_id, update_url=options.update_url, bootstrap=True) if options.update_link: rdf_name = UPDATE_RDF_FILENAME % target_cfg.name print "Exporting update description to %s." % rdf_name update = RDFUpdate() update.add(manifest, options.update_link) open(rdf_name, "w").write(str(update)) xpi_name = XPI_FILENAME % target_cfg.name print "Exporting extension to %s." % xpi_name build_xpi(template_root_dir=app_extension_dir, manifest=manifest, xpi_name=xpi_name, harness_options=harness_options) else: if options.use_server: from cuddlefish.server import run_app 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, harness_options=harness_options, app_type=options.app, binary=options.binary, profiledir=options.profiledir, verbose=options.verbose, timeout=timeout, logfile=options.logfile, addons=options.addons) except Exception, e: if str(e).startswith(MOZRUNNER_BIN_NOT_FOUND): print >> sys.stderr, MOZRUNNER_BIN_NOT_FOUND_HELP.strip() retval = -1 else: raise
def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None, defaults=None, env_root=os.environ.get('CUDDLEFISH_ROOT')): parser_kwargs = dict(arguments=arguments, parser_options=parser_options, parser_groups=parser_groups, usage=usage, 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 == "testpkgs": test_all_packages(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": test_cfx(env_root, options.verbose) return elif command == "sdocs": import docgen import chromeless dirname = os.path.join(chromeless.Dirs().build_dir, "docs") docgen.generate_static_docs(env_root, dirname) print "Created docs in %s." % dirname return 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) use_main = False if command == "xpcom": if 'xpcom' not in target_cfg: print >> sys.stderr, "package.json does not have a 'xpcom' entry." sys.exit(1) if not (options.moz_srcdir and options.moz_objdir): print >> sys.stderr, "srcdir and objdir not specified." sys.exit(1) options.moz_srcdir = os.path.expanduser(options.moz_srcdir) options.moz_objdir = os.path.expanduser(options.moz_objdir) xpcom = target_cfg.xpcom from cuddlefish.xpcom import build_xpcom_components if 'typelibs' in xpcom: xpt_output_dir = packaging.resolve_dir(target_cfg, xpcom.typelibs) else: xpt_output_dir = None build_xpcom_components( comp_src_dir=packaging.resolve_dir(target_cfg, xpcom.src), moz_srcdir=options.moz_srcdir, moz_objdir=options.moz_objdir, base_output_dir=packaging.resolve_dir(target_cfg, xpcom.dest), xpt_output_dir=xpt_output_dir, module_name=xpcom.module) sys.exit(0) elif command == "package": use_main = True elif command == "appify": use_main = True elif command == "test": if 'tests' not in target_cfg: target_cfg['tests'] = [] elif command == "run": use_main = True else: print >> sys.stderr, "Unknown command: %s" % command print >> sys.stderr, "Try using '--help' for assistance." sys.exit(1) 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) 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 ('package', 'run', 'appify'): from cuddlefish.preflight import preflight_config if target_cfg_json: config_was_ok, modified = preflight_config( target_cfg, target_cfg_json, keydir=options.keydir, err_if_privkey_not_found=False) 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 jid = target_cfg["id"] assert not jid.endswith("@jetpack") unique_prefix = '%s-' % jid # used for resource: URLs # the harness_guid is used for an XPCOM class ID. We use the # JetpackID for the add-on ID and the XPCOM contract ID. import uuid harness_guid = str(uuid.uuid4()) else: if options.use_server: harness_guid = '2974c5b5-b671-46f8-a4bb-63c6eca6261b' else: harness_guid = '6724fc1b-3ec4-40e2-8583-8061088b3185' unique_prefix = '%s-' % target jid = harness_guid assert not jid.endswith("@jetpack") bundle_id = jid + "@jetpack" # the resource: URLs prefix is treated too much like a DNS hostname unique_prefix = unique_prefix.lower() assert "@" not in unique_prefix assert "." not in unique_prefix timeout = None targets = [target] if not use_main: timeout = TEST_RUN_TIMEOUT targets.append(options.test_runner_pkg) if options.extra_packages: targets.extend(options.extra_packages.split(",")) deps = packaging.get_deps_for_targets(pkg_cfg, targets) build = packaging.generate_build_for_target( pkg_cfg, target, deps, prefix=unique_prefix, # used to create resource: URLs include_dep_tests=options.dep_tests) if 'resources' in build: resources = build.resources for name in resources: resources[name] = os.path.abspath(resources[name]) dep_xpt_dirs = [] for dep in deps: dep_cfg = pkg_cfg.packages[dep] if 'xpcom' in dep_cfg and 'typelibs' in dep_cfg.xpcom: abspath = packaging.resolve_dir(dep_cfg, dep_cfg.xpcom.typelibs) dep_xpt_dirs.append(abspath) xpts = get_xpts(dep_xpt_dirs) harness_contract_id = ('@mozilla.org/harness-service;1?id=%s' % jid) harness_options = { 'bootstrap': { 'contractID': harness_contract_id, 'classID': '{%s}' % harness_guid }, 'jetpackID': jid, 'bundleID': bundle_id, 'staticArgs': options.static_args, } harness_options.update(build) inherited_options = ['verbose'] if use_main: harness_options['main'] = target_cfg.get('main') else: harness_options['main'] = "run-tests" inherited_options.extend(['iterations', 'filter', 'profileMemory']) for option in inherited_options: harness_options[option] = getattr(options, option) harness_options['metadata'] = packaging.get_metadata(pkg_cfg, deps) packaging.call_plugins(pkg_cfg, deps) retval = 0 a = appifier.Appifier() if command == 'package': browser_code_path = json.loads(options.static_args)["browser"] a.output_xul_app(browser_code=browser_code_path, harness_options=harness_options, dev_mode=False) elif command == 'appify': browser_code_path = json.loads(options.static_args)["browser"] a.output_application(browser_code=browser_code_path, harness_options=harness_options, dev_mode=False) else: # on OSX we must invoke xulrunner from within a proper .app bundle, # otherwise many basic application features will not work. For instance # keyboard focus and mouse interactions will be broken. # for this reason, on OSX we'll actually generate a full standalone # application and launch that using the open command. On other # platforms we'll build a xulrunner application (directory) and # invoke xulrunner-bin pointing at that. browser_code_path = json.loads(options.static_args)["browser"] if options.profiledir: options.profiledir = os.path.expanduser(options.profiledir) options.profiledir = os.path.abspath(options.profiledir) if (platform.system() == 'Darwin'): # because of the manner in which we run the application, we must use a # temporary file to enable console output [fd, tmppath] = tempfile.mkstemp() os.close(fd) print "logging to '%s'" % tmppath harness_options['logFile'] = tmppath standalone_app_dir = a.output_application( browser_code=browser_code_path, harness_options=harness_options, dev_mode=True) print "opening '%s'" % standalone_app_dir tailProcess = None try: tailProcess = subprocess.Popen(["tail", "-f", tmppath]) retval = subprocess.call(["open", "-W", standalone_app_dir]) except KeyboardInterrupt: print "got ^C, exiting..." killProcessByName(standalone_app_dir) finally: tailProcess.terminate() os.remove(tmppath) else: xul_app_dir = a.output_xul_app(browser_code=browser_code_path, harness_options=harness_options, dev_mode=True) from cuddlefish.runner import run_app if options.addons is not None: options.addons = options.addons.split(",") try: retval = run_app(harness_root_dir=xul_app_dir, harness_options=harness_options, xpts=xpts, app_type=options.app, binary=options.binary, profiledir=options.profiledir, verbose=options.verbose, timeout=timeout, logfile=options.logfile, addons=options.addons) except Exception, e: if e.message.startswith(MOZRUNNER_BIN_NOT_FOUND): print >> sys.stderr, MOZRUNNER_BIN_NOT_FOUND_HELP.strip() retval = -1 else: raise sys.exit(retval)
def run( arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None, defaults=None, env_root=os.environ.get("CUDDLEFISH_ROOT") ): parser_kwargs = dict( arguments=arguments, parser_options=parser_options, parser_groups=parser_groups, usage=usage, 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 == "develop": run_development_mode(env_root, defaults=options.__dict__) return if command == "testpkgs": test_all_packages(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": test_cfx(env_root, options.verbose) return elif command == "docs": import subprocess import time import cuddlefish.server print "One moment." popen = subprocess.Popen([sys.executable, cuddlefish.server.__file__, "daemonic"]) # TODO: See if there's actually a way to block on # a particular event occurring, rather than this # relatively arbitrary/generous amount. time.sleep(cuddlefish.server.IDLE_WEBPAGE_TIMEOUT * 2) return elif command == "sdocs": import cuddlefish.server # TODO: Allow user to change this filename via cmd line. filename = "jetpack-sdk-docs.tgz" cuddlefish.server.generate_static_docs(env_root, filename) print "Wrote %s." % filename return 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) use_main = False if command == "xpcom": if "xpcom" not in target_cfg: print >> sys.stderr, "package.json does not have a 'xpcom' entry." sys.exit(1) if not (options.moz_srcdir and options.moz_objdir): print >> sys.stderr, "srcdir and objdir not specified." sys.exit(1) options.moz_srcdir = os.path.expanduser(options.moz_srcdir) options.moz_objdir = os.path.expanduser(options.moz_objdir) xpcom = target_cfg.xpcom from cuddlefish.xpcom import build_xpcom_components if "typelibs" in xpcom: xpt_output_dir = packaging.resolve_dir(target_cfg, xpcom.typelibs) else: xpt_output_dir = None build_xpcom_components( comp_src_dir=packaging.resolve_dir(target_cfg, xpcom.src), moz_srcdir=options.moz_srcdir, moz_objdir=options.moz_objdir, base_output_dir=packaging.resolve_dir(target_cfg, xpcom.dest), xpt_output_dir=xpt_output_dir, module_name=xpcom.module, ) sys.exit(0) elif command == "xpi": use_main = True elif command == "test": if "tests" not in target_cfg: target_cfg["tests"] = [] elif command == "run": use_main = True else: print >> sys.stderr, "Unknown command: %s" % command print >> sys.stderr, "Try using '--help' for assistance." sys.exit(1) 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) 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, keydir=options.keydir, err_if_privkey_not_found=False ) 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 jid = target_cfg["id"] assert not jid.endswith("@jetpack") unique_prefix = "%s-" % jid # used for resource: URLs # the harness_guid is used for an XPCOM class ID. We use the # JetpackID for the add-on ID and the XPCOM contract ID. import uuid harness_guid = str(uuid.uuid4()) else: if options.use_server: harness_guid = "2974c5b5-b671-46f8-a4bb-63c6eca6261b" else: harness_guid = "6724fc1b-3ec4-40e2-8583-8061088b3185" unique_prefix = "%s-" % target jid = harness_guid assert not jid.endswith("@jetpack") bundle_id = jid + "@jetpack" # the resource: URLs prefix is treated too much like a DNS hostname unique_prefix = unique_prefix.lower() assert "@" not in unique_prefix assert "." not in unique_prefix timeout = None targets = [target] if not use_main: timeout = TEST_RUN_TIMEOUT targets.append(options.test_runner_pkg) if options.extra_packages: targets.extend(options.extra_packages.split(",")) deps = packaging.get_deps_for_targets(pkg_cfg, targets) build = packaging.generate_build_for_target( pkg_cfg, target, deps, prefix=unique_prefix, # used to create resource: URLs include_dep_tests=options.dep_tests, ) if "resources" in build: resources = build.resources for name in resources: resources[name] = os.path.abspath(resources[name]) dep_xpt_dirs = [] for dep in deps: dep_cfg = pkg_cfg.packages[dep] if "xpcom" in dep_cfg and "typelibs" in dep_cfg.xpcom: abspath = packaging.resolve_dir(dep_cfg, dep_cfg.xpcom.typelibs) dep_xpt_dirs.append(abspath) xpts = get_xpts(dep_xpt_dirs) harness_contract_id = "@mozilla.org/harness-service;1?id=%s" % jid harness_options = { "bootstrap": {"contractID": harness_contract_id, "classID": "{%s}" % harness_guid}, "jetpackID": jid, "bundleID": bundle_id, } harness_options.update(build) inherited_options = ["verbose"] if use_main: harness_options["main"] = target_cfg.get("main") else: harness_options["main"] = "run-tests" inherited_options.extend(["iterations", "filter", "profileMemory"]) for option in inherited_options: harness_options[option] = getattr(options, option) harness_options["metadata"] = packaging.get_metadata(pkg_cfg, deps) packaging.call_plugins(pkg_cfg, deps) retval = 0 if options.templatedir: app_extension_dir = os.path.abspath(options.templatedir) else: mydir = os.path.dirname(os.path.abspath(__file__)) if sys.platform == "darwin": # If we're on OS X, at least point into the XULRunner # app dir so we run as a proper app if using XULRunner. app_extension_dir = os.path.join(mydir, "Test App.app", "Contents", "Resources") else: app_extension_dir = os.path.join(mydir, "app-extension") if command == "xpi": from cuddlefish.xpi import build_xpi from cuddlefish.rdf import gen_manifest, RDFUpdate manifest = gen_manifest( template_root_dir=app_extension_dir, target_cfg=target_cfg, bundle_id=bundle_id, update_url=options.update_url, bootstrap=True, ) if options.update_link: rdf_name = UPDATE_RDF_FILENAME % target_cfg.name print "Exporting update description to %s." % rdf_name update = RDFUpdate() update.add(manifest, options.update_link) open(rdf_name, "w").write(str(update)) xpi_name = XPI_FILENAME % target_cfg.name print "Exporting extension to %s." % xpi_name build_xpi( template_root_dir=app_extension_dir, manifest=manifest, xpi_name=xpi_name, harness_options=harness_options, xpts=xpts, ) else: if options.use_server: from cuddlefish.server import run_app 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, harness_options=harness_options, xpts=xpts, app_type=options.app, binary=options.binary, profiledir=options.profiledir, verbose=options.verbose, timeout=timeout, logfile=options.logfile, addons=options.addons, ) except Exception, e: if e.message.startswith(MOZRUNNER_BIN_NOT_FOUND): print >> sys.stderr, MOZRUNNER_BIN_NOT_FOUND_HELP.strip() retval = -1 else: raise
def run( arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None, defaults=None, env_root=os.environ.get("CUDDLEFISH_ROOT"), stdout=sys.stdout, ): parser_kwargs = dict( arguments=arguments, global_options=global_options, parser_groups=parser_groups, usage=usage, 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 == "develop": run_development_mode(env_root, defaults=options.__dict__) return if command == "testpkgs": test_all_packages(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": test_cfx(env_root, options.verbose) return elif command == "docs": import subprocess import time import cuddlefish.server print >> stdout, "One moment." popen = subprocess.Popen([sys.executable, cuddlefish.server.__file__, "daemonic"]) # TODO: See if there's actually a way to block on # a particular event occurring, rather than this # relatively arbitrary/generous amount. time.sleep(cuddlefish.server.IDLE_WEBPAGE_TIMEOUT * 2) return elif command == "sdocs": import cuddlefish.server # TODO: Allow user to change this filename via cmd line. filename = "addon-sdk-docs.tgz" cuddlefish.server.generate_static_docs(env_root, filename, options.baseurl) print >> stdout, "Wrote %s." % filename return 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 timeout = None inherited_options = ["verbose", "enable_e10s"] if command == "xpi": use_main = True elif command == "test": if "tests" not in target_cfg: target_cfg["tests"] = [] timeout = TEST_RUN_TIMEOUT inherited_options.extend(["iterations", "filter", "profileMemory"]) elif command == "run": use_main = True else: print >> sys.stderr, "Unknown command: %s" % command print >> sys.stderr, "Try using '--help' for assistance." sys.exit(1) 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 # the harness_guid is used for an XPCOM class ID. We use the # JetpackID for the add-on ID and the XPCOM contract ID. if "harnessClassID" in target_cfg: # For the sake of non-bootstrapped extensions, we allow to specify the # classID of harness' XPCOM component in package.json. This makes it # possible to register the component using a static chrome.manifest file harness_guid = target_cfg["harnessClassID"] else: import uuid harness_guid = str(uuid.uuid4()) # 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" if "id" in target_cfg: jid = target_cfg["id"] if not ("@" in jid or jid.startswith("{")): jid = jid + "@jetpack" unique_prefix = "%s-" % jid # used for resource: URLs else: # The Jetpack ID is not required for cfx test, in which case we have to # make one up based on the GUID. if options.use_server: # The harness' contractID (hence also the jid and the harness_guid) # need to be static in the "development mode", so that bootstrap.js # can unload the previous version of the package being developed. harness_guid = "2974c5b5-b671-46f8-a4bb-63c6eca6261b" unique_prefix = "%s-" % target jid = harness_guid bundle_id = jid # the resource: URL's prefix is treated too much like a DNS hostname unique_prefix = unique_prefix.lower() unique_prefix = unique_prefix.replace("@", "-at-") unique_prefix = unique_prefix.replace(".", "-dot-") 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 uri_prefix = "resource://%s" % unique_prefix # 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 URI 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 == "api-utils" assert pkg_cfg.packages["api-utils"].loader == "lib/cuddlefish.js" cuddlefish_js_path = os.path.join(pkg_cfg.packages["api-utils"].root_dir, "lib", "cuddlefish.js") loader_modules = [("api-utils", "lib", "cuddlefish", cuddlefish_js_path)] manifest = build_manifest(target_cfg, pkg_cfg, deps, uri_prefix, False, loader_modules) 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, prefix=unique_prefix, # used to create resource: URLs include_dep_tests=options.dep_tests, ) if "resources" in build: resources = build.resources for name in resources: resources[name] = os.path.abspath(resources[name]) harness_contract_id = "@mozilla.org/harness-service;1?id=%s" % jid harness_options = { "bootstrap": {"contractID": harness_contract_id, "classID": "{%s}" % harness_guid}, "jetpackID": jid, "bundleID": bundle_id, "uriPrefix": uri_prefix, "staticArgs": options.static_args, "name": target, } harness_options.update(build) if command == "test": # This should be contained in the test runner package. harness_options["main"] = "run-tests" else: harness_options["main"] = target_cfg.get("main") for option in inherited_options: harness_options[option] = getattr(options, option) harness_options["metadata"] = packaging.get_metadata(pkg_cfg, used_deps) sdk_version = get_version(env_root) 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__)) if sys.platform == "darwin": # If we're on OS X, at least point into the XULRunner # app dir so we run as a proper app if using XULRunner. app_extension_dir = os.path.join(mydir, "Test App.app", "Contents", "Resources") else: app_extension_dir = os.path.join(mydir, "app-extension") harness_options["manifest"] = manifest.get_harness_options_manifest(uri_prefix) if command == "xpi": from cuddlefish.xpi import build_xpi from cuddlefish.rdf import gen_manifest, RDFUpdate manifest_rdf = gen_manifest( template_root_dir=app_extension_dir, target_cfg=target_cfg, bundle_id=bundle_id, update_url=options.update_url, bootstrap=True, ) if 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 = set(manifest.get_used_files()) if not options.strip_xpi: used_files = None # disables the filter xpi_name = XPI_FILENAME % target_cfg.name print >> stdout, "Exporting extension to %s." % xpi_name build_xpi( template_root_dir=app_extension_dir, manifest=manifest_rdf, xpi_name=xpi_name, harness_options=harness_options, limit_to=used_files, ) else: if options.use_server: from cuddlefish.server import run_app 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, harness_options=harness_options, app_type=options.app, binary=options.binary, profiledir=options.profiledir, verbose=options.verbose, timeout=timeout, logfile=options.logfile, addons=options.addons, args=options.cmdargs, norun=options.no_run, ) except Exception, e: if str(e).startswith(MOZRUNNER_BIN_NOT_FOUND): print >> sys.stderr, MOZRUNNER_BIN_NOT_FOUND_HELP.strip() retval = -1 else: raise
def test_preflight(self): basedir = self.make_basedir() fn = os.path.join(basedir, "package.json") # empty config is not ok: need id (name is automatically supplied) config_orig = "{}" self.write(config_orig) out = StringIO() cfg = self.get_cfg() config_was_ok, modified = preflight.preflight_config(cfg, fn, stderr=out) self.failUnlessEqual(config_was_ok, False) self.failUnlessEqual(modified, True) backup_fn = os.path.join(basedir, "package.json.backup") config_backup = open(backup_fn,"r").read() self.failUnlessEqual(config_backup, config_orig) config = json.loads(self.read()) self.failIf("name" in config) self.failUnless("id" in config) self.failUnless(config["id"].startswith("jid1-"), config["id"]) self.failUnlessEqual(out.getvalue().strip(), "No 'id' in package.json: creating a new ID for you.") os.unlink(backup_fn) # just a name? we add the id config_orig = '{"name": "my-awesome-package"}' self.write(config_orig) out = StringIO() cfg = self.get_cfg() config_was_ok, modified = preflight.preflight_config(cfg, fn, stderr=out) self.failUnlessEqual(config_was_ok, False) self.failUnlessEqual(modified, True) backup_fn = os.path.join(basedir, "package.json.backup") config_backup = open(backup_fn,"r").read() self.failUnlessEqual(config_backup, config_orig) config = json.loads(self.read()) self.failUnlessEqual(config["name"], "my-awesome-package") self.failUnless("id" in config) self.failUnless(config["id"].startswith("jid1-"), config["id"]) jid = str(config["id"]) self.failUnlessEqual(out.getvalue().strip(), "No 'id' in package.json: creating a new ID for you.") os.unlink(backup_fn) # name and valid id? great! ship it! config2 = '{"name": "my-awesome-package", "id": "%s"}' % jid self.write(config2) out = StringIO() cfg = self.get_cfg() config_was_ok, modified = preflight.preflight_config(cfg, fn, stderr=out) self.failUnlessEqual(config_was_ok, True) self.failUnlessEqual(modified, False) config2a = self.read() self.failUnlessEqual(config2a, config2) self.failUnlessEqual(out.getvalue().strip(), "") # name and anonymous ID? without asking to see its papers, ship it config3 = '{"name": "my-old-skool-package", "id": "anonid0-deadbeef"}' self.write(config3) out = StringIO() cfg = self.get_cfg() config_was_ok, modified = preflight.preflight_config(cfg, fn, stderr=out) self.failUnlessEqual(config_was_ok, True) self.failUnlessEqual(modified, False) config3a = self.read() self.failUnlessEqual(config3a, config3) self.failUnlessEqual(out.getvalue().strip(), "") # name and old-style ID? with nostalgic trepidation, ship it config4 = '{"name": "my-old-skool-package", "id": "*****@*****.**"}' self.write(config4) out = StringIO() cfg = self.get_cfg() config_was_ok, modified = preflight.preflight_config(cfg, fn, stderr=out) self.failUnlessEqual(config_was_ok, True) self.failUnlessEqual(modified, False) config4a = self.read() self.failUnlessEqual(config4a, config4) self.failUnlessEqual(out.getvalue().strip(), "")
def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None, defaults=None, env_root=os.environ.get('CUDDLEFISH_ROOT'), stdout=sys.stdout): parser_kwargs = dict(arguments=arguments, global_options=global_options, parser_groups=parser_groups, usage=usage, 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 == "testex": test_all_examples(env_root, defaults=options.__dict__) return elif command == "testall": test_all(env_root, defaults=options.__dict__) return elif command == "testcfx": test_cfx(env_root, options.verbose) return elif command == "docs": from cuddlefish.docs import generate if len(args) > 1: docs_home = generate.generate_docs(env_root, filename=args[1]) else: docs_home = generate.generate_docs(env_root) webbrowser.open(docs_home) return elif command == "sdocs": from cuddlefish.docs import generate # TODO: Allow user to change this filename via cmd line. filename = generate.generate_static_docs(env_root, base_url=options.baseurl) print >>stdout, "Wrote %s." % filename return 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']) enforce_timeouts = True elif command == "run": use_main = True else: print >>sys.stderr, "Unknown command: %s" % command print >>sys.stderr, "Try using '--help' for assistance." sys.exit(1) 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 # the harness_guid is used for an XPCOM class ID. We use the # JetpackID for the add-on ID and the XPCOM contract ID. if "harnessClassID" in target_cfg: # For the sake of non-bootstrapped extensions, we allow to specify the # classID of harness' XPCOM component in package.json. This makes it # possible to register the component using a static chrome.manifest file harness_guid = target_cfg["harnessClassID"] else: import uuid harness_guid = str(uuid.uuid4()) # 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" if "id" in target_cfg: jid = target_cfg["id"] else: jid = harness_guid if not ("@" in jid or jid.startswith("{")): jid = jid + "@jetpack" unique_prefix = '%s-' % jid # used for resource: URLs bundle_id = jid # the resource: URL's prefix is treated too much like a DNS hostname unique_prefix = unique_prefix.lower() unique_prefix = unique_prefix.replace("@", "-at-") unique_prefix = unique_prefix.replace(".", "-dot-") 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 uri_prefix = "resource://%s" % unique_prefix # 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 URI 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 == "api-utils" assert pkg_cfg.packages["api-utils"].loader == "lib/cuddlefish.js" cuddlefish_js_path = os.path.join(pkg_cfg.packages["api-utils"].root_dir, "lib", "cuddlefish.js") loader_modules = [("api-utils", "lib", "cuddlefish", cuddlefish_js_path)] scan_tests = command == "test" try: manifest = build_manifest(target_cfg, pkg_cfg, deps, uri_prefix, scan_tests, loader_modules) except ModuleNotFoundError, e: print str(e) sys.exit(1)
def test_preflight(self): basedir = self.make_basedir() fn = os.path.join(basedir, "package.json") keydir = os.path.join(basedir, "keys") # empty config is not ok: need id (name is automatically supplied) config_orig = "{}" self.write(config_orig) out = StringIO() cfg = self.get_cfg() config_was_ok, modified = preflight.preflight_config(cfg, fn, stderr=out, keydir=keydir) self.failUnlessEqual(config_was_ok, False) self.failUnlessEqual(modified, True) backup_fn = os.path.join(basedir, "package.json.backup") config_backup = open(backup_fn,"r").read() self.failUnlessEqual(config_backup, config_orig) config = json.loads(self.read()) self.failIf("name" in config) self.failUnless("id" in config) self.failUnlessEqual(out.getvalue().strip(), "No 'id' in package.json: creating a new keypair for you.") jid = str(config["id"]) keyfile = os.path.join(keydir, jid) fields, fieldnames = self.parse(open(keyfile).read()) self.failUnlessEqual(fieldnames[0], "private-key") privkey = fields["private-key"] self.failUnless(privkey.startswith("private-jid0-"), privkey) self.failUnlessEqual(fields["jid"], jid) self.failUnlessEqual(fields["name"], "pretend name") os.unlink(backup_fn) # just a name? we add the id config_orig = '{"name": "my-awesome-package"}' self.write(config_orig) out = StringIO() cfg = self.get_cfg() config_was_ok, modified = preflight.preflight_config(cfg, fn, stderr=out, keydir=keydir) self.failUnlessEqual(config_was_ok, False) self.failUnlessEqual(modified, True) backup_fn = os.path.join(basedir, "package.json.backup") config_backup = open(backup_fn,"r").read() self.failUnlessEqual(config_backup, config_orig) config = json.loads(self.read()) self.failUnlessEqual(config["name"], "my-awesome-package") self.failUnless("id" in config) self.failUnlessEqual(out.getvalue().strip(), "No 'id' in package.json: creating a new keypair for you.") jid = str(config["id"]) keyfile = os.path.join(keydir, jid) fields, fieldnames = self.parse(open(keyfile).read()) privkey = fields["private-key"] self.failUnless(privkey.startswith("private-jid0-"), privkey) self.failUnlessEqual(fields["jid"], jid) self.failUnlessEqual(fields["name"], "my-awesome-package") # name and valid id? great! ship it! config2 = '{"name": "my-awesome-package", "id": "%s"}' % jid self.write(config2) out = StringIO() cfg = self.get_cfg() config_was_ok, modified = preflight.preflight_config(cfg, fn, stderr=out, keydir=keydir) self.failUnlessEqual(config_was_ok, True) self.failUnlessEqual(modified, False) config2a = self.read() self.failUnlessEqual(config2a, config2) self.failUnlessEqual(out.getvalue().strip(), "") # name and invalid id? tell them to get a new one os.unlink(keyfile) self.write(config2) out = StringIO() cfg = self.get_cfg() config_was_ok, modified = preflight.preflight_config(cfg, fn, stderr=out, keydir=keydir) self.failUnlessEqual(config_was_ok, False) self.failUnlessEqual(modified, False) out = out.getvalue().strip() self.failUnless("Your package.json says our ID is" in out, out) self.failUnless("But I don't have a corresponding private key in" in out, out) self.failUnless("If you are the original developer" in out, out) self.failUnless("Otherwise, if you are a new developer" in out, out) # name and anonymous ID? without asking to see its papers, ship it config3 = '{"name": "my-old-skool-package", "id": "anonid0-deadbeef"}' self.write(config3) out = StringIO() cfg = self.get_cfg() config_was_ok, modified = preflight.preflight_config(cfg, fn, stderr=out, keydir=keydir, err_if_privkey_not_found=False) self.failUnlessEqual(config_was_ok, True) self.failUnlessEqual(modified, False) config3a = self.read() self.failUnlessEqual(config3a, config3) self.failUnlessEqual(out.getvalue().strip(), "") # name and old-style ID? with nostalgic trepidation, ship it config4 = '{"name": "my-old-skool-package", "id": "*****@*****.**"}' self.write(config4) out = StringIO() cfg = self.get_cfg() config_was_ok, modified = preflight.preflight_config(cfg, fn, stderr=out, keydir=keydir, err_if_privkey_not_found=False) self.failUnlessEqual(config_was_ok, True) self.failUnlessEqual(modified, False) config4a = self.read() self.failUnlessEqual(config4a, config4) self.failUnlessEqual(out.getvalue().strip(), "")
def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None, defaults=None, env_root=os.environ.get('CUDDLEFISH_ROOT')): parser_kwargs = dict(arguments=arguments, parser_options=parser_options, parser_groups=parser_groups, usage=usage, 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 == "testpkgs": test_all_packages(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": test_cfx(env_root, options.verbose) return elif command == "sdocs": import docgen import chromeless dirname = os.path.join(chromeless.Dirs().build_dir, "docs") docgen.generate_static_docs(env_root, dirname) print "Created docs in %s." % dirname return 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) use_main = False if command == "xpcom": if 'xpcom' not in target_cfg: print >>sys.stderr, "package.json does not have a 'xpcom' entry." sys.exit(1) if not (options.moz_srcdir and options.moz_objdir): print >>sys.stderr, "srcdir and objdir not specified." sys.exit(1) options.moz_srcdir = os.path.expanduser(options.moz_srcdir) options.moz_objdir = os.path.expanduser(options.moz_objdir) xpcom = target_cfg.xpcom from cuddlefish.xpcom import build_xpcom_components if 'typelibs' in xpcom: xpt_output_dir = packaging.resolve_dir(target_cfg, xpcom.typelibs) else: xpt_output_dir = None build_xpcom_components( comp_src_dir=packaging.resolve_dir(target_cfg, xpcom.src), moz_srcdir=options.moz_srcdir, moz_objdir=options.moz_objdir, base_output_dir=packaging.resolve_dir(target_cfg, xpcom.dest), xpt_output_dir=xpt_output_dir, module_name=xpcom.module ) sys.exit(0) elif command == "package": use_main = True elif command == "appify": use_main = True elif command == "test": if 'tests' not in target_cfg: target_cfg['tests'] = [] elif command == "run": use_main = True else: print >>sys.stderr, "Unknown command: %s" % command print >>sys.stderr, "Try using '--help' for assistance." sys.exit(1) 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) 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 ('package', 'run', 'appify'): from cuddlefish.preflight import preflight_config if target_cfg_json: config_was_ok, modified = preflight_config( target_cfg, target_cfg_json, keydir=options.keydir, err_if_privkey_not_found=False ) 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 jid = target_cfg["id"] assert not jid.endswith("@jetpack") unique_prefix = '%s-' % jid # used for resource: URLs # the harness_guid is used for an XPCOM class ID. We use the # JetpackID for the add-on ID and the XPCOM contract ID. import uuid harness_guid = str(uuid.uuid4()) else: if options.use_server: harness_guid = '2974c5b5-b671-46f8-a4bb-63c6eca6261b' else: harness_guid = '6724fc1b-3ec4-40e2-8583-8061088b3185' unique_prefix = '%s-' % target jid = harness_guid assert not jid.endswith("@jetpack") bundle_id = jid + "@jetpack" # the resource: URLs prefix is treated too much like a DNS hostname unique_prefix = unique_prefix.lower() assert "@" not in unique_prefix assert "." not in unique_prefix timeout = None targets = [target] if not use_main: timeout = TEST_RUN_TIMEOUT targets.append(options.test_runner_pkg) if options.extra_packages: targets.extend(options.extra_packages.split(",")) deps = packaging.get_deps_for_targets(pkg_cfg, targets) build = packaging.generate_build_for_target( pkg_cfg, target, deps, prefix=unique_prefix, # used to create resource: URLs include_dep_tests=options.dep_tests ) if 'resources' in build: resources = build.resources for name in resources: resources[name] = os.path.abspath(resources[name]) dep_xpt_dirs = [] for dep in deps: dep_cfg = pkg_cfg.packages[dep] if 'xpcom' in dep_cfg and 'typelibs' in dep_cfg.xpcom: abspath = packaging.resolve_dir(dep_cfg, dep_cfg.xpcom.typelibs) dep_xpt_dirs.append(abspath) xpts = get_xpts(dep_xpt_dirs) harness_contract_id = ('@mozilla.org/harness-service;1?id=%s' % jid) harness_options = { 'bootstrap': { 'contractID': harness_contract_id, 'classID': '{%s}' % harness_guid }, 'jetpackID': jid, 'bundleID': bundle_id, 'staticArgs': options.static_args, } harness_options.update(build) inherited_options = ['verbose'] if use_main: harness_options['main'] = target_cfg.get('main') else: harness_options['main'] = "run-tests" inherited_options.extend(['iterations', 'filter', 'profileMemory']) for option in inherited_options: harness_options[option] = getattr(options, option) harness_options['metadata'] = packaging.get_metadata(pkg_cfg, deps) packaging.call_plugins(pkg_cfg, deps) retval = 0 a = appifier.Appifier() if command == 'package': browser_code_path = json.loads(options.static_args)["browser"] a.output_xul_app(browser_code=browser_code_path, harness_options=harness_options, dev_mode=False) elif command == 'appify': browser_code_path = json.loads(options.static_args)["browser"] a.output_application(browser_code=browser_code_path, harness_options=harness_options, dev_mode=False) else: # on OSX we must invoke xulrunner from within a proper .app bundle, # otherwise many basic application features will not work. For instance # keyboard focus and mouse interactions will be broken. # for this reason, on OSX we'll actually generate a full standalone # application and launch that using the open command. On other # platforms we'll build a xulrunner application (directory) and # invoke xulrunner-bin pointing at that. browser_code_path = json.loads(options.static_args)["browser"] if options.profiledir: options.profiledir = os.path.expanduser(options.profiledir) options.profiledir = os.path.abspath(options.profiledir) if (platform.system() == 'Darwin'): # because of the manner in which we run the application, we must use a # temporary file to enable console output [fd, tmppath] = tempfile.mkstemp() os.close(fd) print "logging to '%s'" % tmppath harness_options['logFile'] = tmppath standalone_app_dir = a.output_application(browser_code=browser_code_path, harness_options=harness_options, dev_mode=True) print "opening '%s'" % standalone_app_dir tailProcess = None try: tailProcess = subprocess.Popen(["tail", "-f", tmppath]) retval = subprocess.call(["open", "-W", standalone_app_dir]) except KeyboardInterrupt: print "got ^C, exiting..." killProcessByName(standalone_app_dir) finally: tailProcess.terminate() os.remove(tmppath) else: xul_app_dir = a.output_xul_app(browser_code=browser_code_path, harness_options=harness_options, dev_mode=True) from cuddlefish.runner import run_app if options.addons is not None: options.addons = options.addons.split(",") try: retval = run_app(harness_root_dir=xul_app_dir, harness_options=harness_options, xpts=xpts, app_type=options.app, binary=options.binary, profiledir=options.profiledir, verbose=options.verbose, timeout=timeout, logfile=options.logfile, addons=options.addons) except Exception, e: if e.message.startswith(MOZRUNNER_BIN_NOT_FOUND): print >>sys.stderr, MOZRUNNER_BIN_NOT_FOUND_HELP.strip() retval = -1 else: raise sys.exit(retval)
def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None, defaults=None, env_root=os.environ.get('CUDDLEFISH_ROOT')): parser_kwargs = dict(arguments=arguments, global_options=global_options, parser_groups=parser_groups, usage=usage, 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 == "testex": test_all_examples(env_root, defaults=options.__dict__) return elif command == "testall": test_all(env_root, defaults=options.__dict__) return elif command == "testcfx": test_cfx(env_root, options.verbose) return elif command == "sdocs": import docgen import chromeless dirname = os.path.join(chromeless.Dirs().build_dir, "docs") docgen.generate_static_docs(env_root, dirname) print "Created docs in %s." % dirname return 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 timeout = None inherited_options = ['verbose', 'enable_e10s'] if command == "xpi": use_main = True if command == "appify": use_main = True elif command == "test": if 'tests' not in target_cfg: target_cfg['tests'] = [] timeout = TEST_RUN_TIMEOUT inherited_options.extend(['iterations', 'filter', 'profileMemory']) elif command == "run": use_main = True else: print >> sys.stderr, "Unknown command: %s" % command print >> sys.stderr, "Try using '--help' for assistance." sys.exit(1) 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) target = target_cfg.name # the harness_guid is used for an XPCOM class ID. We use the # JetpackID for the add-on ID and the XPCOM contract ID. if "harnessClassID" in target_cfg: # For the sake of non-bootstrapped extensions, we allow to specify the # classID of harness' XPCOM component in package.json. This makes it # possible to register the component using a static chrome.manifest file harness_guid = target_cfg["harnessClassID"] else: import uuid harness_guid = str(uuid.uuid4()) # TODO: Consider keeping a cache of dynamic UUIDs, based # on absolute filesystem pathname, in the root directory # or something. if command in ('xpi', 'run', 'appify'): from cuddlefish.preflight import preflight_config if target_cfg_json: config_was_ok, modified = preflight_config( target_cfg, target_cfg_json, keydir=options.keydir, err_if_privkey_not_found=False) 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" if "id" in target_cfg: jid = target_cfg["id"] assert not jid.endswith("@jetpack") unique_prefix = '%s-' % jid # used for resource: URLs else: unique_prefix = '%s-' % target jid = harness_guid assert not jid.endswith("@jetpack") if (jid.startswith("jid0-") or jid.startswith("anonid0-")): bundle_id = jid + "@jetpack" # Don't append "@jetpack" to old-style IDs, as they should be exactly # as specified by the addon author so AMO and Firefox continue to treat # their addon bundles as representing the same addon (and also because # they may already have an @ sign in them, and there can be only one). else: bundle_id = jid # the resource: URL's prefix is treated too much like a DNS hostname unique_prefix = unique_prefix.lower() unique_prefix = unique_prefix.replace("@", "-at-") unique_prefix = unique_prefix.replace(".", "-dot-") targets = [target] if command == "test": targets.append(options.test_runner_pkg) if options.extra_packages: targets.extend(options.extra_packages.split(",")) deps = packaging.get_deps_for_targets(pkg_cfg, targets) build = packaging.generate_build_for_target( pkg_cfg, target, deps, prefix=unique_prefix, # used to create resource: URLs include_dep_tests=options.dep_tests) if 'resources' in build: resources = build.resources for name in resources: resources[name] = os.path.abspath(resources[name]) harness_contract_id = ('@mozilla.org/harness-service;1?id=%s' % jid) harness_options = { 'bootstrap': { 'contractID': harness_contract_id, 'classID': '{%s}' % harness_guid }, 'jetpackID': jid, 'bundleID': bundle_id, 'staticArgs': options.static_args, } harness_options.update(build) if command == "test": # This should be contained in the test runner package. harness_options['main'] = 'run-tests' else: harness_options['main'] = target_cfg.get('main') for option in inherited_options: harness_options[option] = getattr(options, option) harness_options['metadata'] = packaging.get_metadata(pkg_cfg, deps) #sdk_version = get_version(env_root) #harness_options['sdkVersion'] = sdk_version packaging.call_plugins(pkg_cfg, deps) retval = 0 a = appifier.Appifier() if command == 'package': browser_code_path = options.static_args["browser"] a.output_xul_app(browser_code=browser_code_path, harness_options=harness_options, dev_mode=False) elif command == 'appify': browser_code_path = options.static_args["browser"] a.output_application(browser_code=browser_code_path, harness_options=harness_options, dev_mode=False) if command == 'run': browser_code_path = options.static_args["browser"] 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(",") if (platform.system() == 'Darwin'): # because of the manner in which we run the application, we mus t use a # temporary file to enable console output [fd, tmppath] = tempfile.mkstemp() os.close(fd) print "__init__: tmppath" + tmppath print "__init__: browser code path: " + browser_code_path print "And logging to '%s'" % tmppath harness_options['logFile'] = tmppath standalone_app_dir = a.output_application( browser_code=browser_code_path, harness_options=harness_options, dev_mode=True) print "opening '%s'" % standalone_app_dir tailProcess = None try: import subprocess tailProcess = subprocess.Popen(["tail", "-f", tmppath]) retval = subprocess.call(["open", "-W", standalone_app_dir]) except KeyboardInterrupt: print "got ^C, exiting..." killProcessByName(standalone_app_dir) finally: tailProcess.terminate() os.remove(tmppath) 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(",") app_extension_dir = os.path.join(env_root, "impl", "appifier", "resources", "xulrunner.template") try: retval = run_app(harness_root_dir=app_extension_dir, harness_options=harness_options, app_type=options.app, binary=options.binary, profiledir=options.profiledir, verbose=options.verbose, timeout=timeout, logfile=options.logfile, addons=options.addons) except Exception, e: if str(e).startswith(MOZRUNNER_BIN_NOT_FOUND): print >> sys.stderr, MOZRUNNER_BIN_NOT_FOUND_HELP.strip() retval = -1 else: raise
def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None, defaults=None, env_root=os.environ.get('CUDDLEFISH_ROOT')): parser_kwargs = dict(arguments=arguments, global_options=global_options, parser_groups=parser_groups, usage=usage, 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 == "testpkgs": test_all_packages(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": test_cfx(env_root, options.verbose) return elif command == "sdocs": import docgen import chromeless dirname = os.path.join(chromeless.Dirs().build_dir, "docs") docgen.generate_static_docs(env_root, dirname) print "Created docs in %s." % dirname return 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 timeout = None inherited_options = ['verbose', 'enable_e10s'] if command in ("run", "package", "appify"): use_main = True elif command == "test": if 'tests' not in target_cfg: target_cfg['tests'] = [] timeout = TEST_RUN_TIMEOUT inherited_options.extend(['iterations', 'filter', 'profileMemory']) else: print >>sys.stderr, "Unknown command: %s" % command print >>sys.stderr, "Try using '--help' for assistance." sys.exit(1) 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) target = target_cfg.name # the harness_guid is used for an XPCOM class ID. We use the # JetpackID for the add-on ID and the XPCOM contract ID. if "harnessClassID" in target_cfg: # For the sake of non-bootstrapped extensions, we allow to specify the # classID of harness' XPCOM component in package.json. This makes it # possible to register the component using a static chrome.manifest file harness_guid = target_cfg["harnessClassID"] else: import uuid harness_guid = str(uuid.uuid4()) # TODO: Consider keeping a cache of dynamic UUIDs, based # on absolute filesystem pathname, in the root directory # or something. if command in ('package', 'run', 'appify'): from cuddlefish.preflight import preflight_config if target_cfg_json: config_was_ok, modified = preflight_config( target_cfg, target_cfg_json, keydir=options.keydir, err_if_privkey_not_found=False ) 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" if "id" in target_cfg: jid = target_cfg["id"] assert not jid.endswith("@jetpack") unique_prefix = '%s-' % jid # used for resource: URLs else: unique_prefix = '%s-' % target jid = harness_guid assert not jid.endswith("@jetpack") if (jid.startswith("jid0-") or jid.startswith("anonid0-")): bundle_id = jid + "@jetpack" # Don't append "@jetpack" to old-style IDs, as they should be exactly # as specified by the addon author so AMO and Firefox continue to treat # their addon bundles as representing the same addon (and also because # they may already have an @ sign in them, and there can be only one). else: bundle_id = jid # the resource: URL's prefix is treated too much like a DNS hostname unique_prefix = unique_prefix.lower() unique_prefix = unique_prefix.replace("@", "-at-") unique_prefix = unique_prefix.replace(".", "-dot-") targets = [target] if command == "test": targets.append(options.test_runner_pkg) if options.extra_packages: targets.extend(options.extra_packages.split(",")) deps = packaging.get_deps_for_targets(pkg_cfg, targets) build = packaging.generate_build_for_target( pkg_cfg, target, deps, prefix=unique_prefix, # used to create resource: URLs include_dep_tests=options.dep_tests ) if 'resources' in build: resources = build.resources for name in resources: resources[name] = os.path.abspath(resources[name]) harness_contract_id = ('@mozilla.org/harness-service;1?id=%s' % jid) harness_options = { 'bootstrap': { 'contractID': harness_contract_id, 'classID': '{%s}' % harness_guid }, 'jetpackID': jid, 'bundleID': bundle_id, 'staticArgs': options.static_args, } harness_options.update(build) if command == "test": # This should be contained in the test runner package. harness_options['main'] = 'run-tests' else: harness_options['main'] = target_cfg.get('main') for option in inherited_options: harness_options[option] = getattr(options, option) harness_options['metadata'] = packaging.get_metadata(pkg_cfg, deps) #sdk_version = get_version(env_root) #harness_options['sdkVersion'] = sdk_version packaging.call_plugins(pkg_cfg, deps) retval = 0 a = appifier.Appifier() if command == 'package': browser_code_path = options.static_args["browser"] a.output_xul_app(browser_code=browser_code_path, harness_options=harness_options, dev_mode=False) elif command == 'appify': browser_code_path = options.static_args["browser"] a.output_application(browser_code=browser_code_path, harness_options=harness_options, dev_mode=False) else: browser_code_path = options.static_args["browser"] 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(",") if (platform.system() == 'Darwin'): # because of the manner in which we run the application, we mus t use a # temporary file to enable console output [fd, tmppath] = tempfile.mkstemp() os.close(fd) print "__init__: tmppath" + tmppath; print "__init__: browser code path: " + browser_code_path print "And logging to '%s'" % tmppath harness_options['logFile'] = tmppath standalone_app_dir = a.output_application(browser_code=browser_code_path, harness_options=harness_options, dev_mode=True, verbose=False) print "opening '%s'" % standalone_app_dir tailProcess = None try: import subprocess tailProcess = subprocess.Popen(["tail", "-f", tmppath]) retval = subprocess.call(["open", "-W", standalone_app_dir]) except KeyboardInterrupt: print "got ^C, exiting..." killProcessByName(standalone_app_dir) finally: tailProcess.terminate() os.remove(tmppath) else: xul_app_dir = a.output_xul_app(browser_code=browser_code_path, harness_options=harness_options, dev_mode=True, verbose=False) from cuddlefish.runner import run_app try: retval = run_app(harness_root_dir=xul_app_dir, harness_options=harness_options, app_type=options.app, binary=options.binary, profiledir=options.profiledir, verbose=options.verbose, timeout=timeout, logfile=options.logfile, addons=options.addons) 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)