def display(ctx, task, reverse=None, show_cache=False): """ Display a task and its dependencies visually. """ registry = TaskRegistry.get() gb = graph.GraphBuilder(registry, ctx.obj["manifest"]) dag = gb.build(task, influence=show_cache) options = JoltOptions() acache = cache.ArtifactCache.get(options) if reverse: def iterator(task): return list(dag.predecessors(task)) reverse = utils.as_list(reverse) tasklist = dag.select(lambda graph, node: node.short_qualified_name in reverse or node.qualified_name in reverse) else: def iterator(task): return task.children tasklist = dag.requested_goals if dag.has_tasks(): def _display(task, indent=0, last=None): header = "" if indent > 0: for pipe in last[:-1]: if pipe: header += "\u2502 " else: header += " " if last[-1]: header += "\u251c\u2574" else: header += "\u2514\u2574" if not show_cache: colorize = str elif task.is_cacheable() and not acache.is_available(task): colorize = colors.red else: colorize = colors.green print(header + colorize(task.short_qualified_name)) children = iterator(task) for i in range(0, len(children)): _display(children[i], indent + 1, last=(last or []) + [i + 1 != len(children)]) for task in tasklist: _display(task) else: log.info("no tasks to display")
def load(self): super(NativeRecipe, self).load() name = utils.canonical(self.path) loader = SourceFileLoader("joltfile_{0}".format(name), self.path) module = ModuleType(loader.name) module.__file__ = self.path loader.exec_module(module) sys.modules[loader.name] = module classes = inspection.getmoduleclasses(module, [Task, TaskGenerator], NativeRecipe._is_abstract) generators = [] for cls in classes[TaskGenerator]: cls.joltdir = self.joltdir or os.path.dirname(self.path) generators.append(cls()) for generator in generators: generated_tasks = utils.as_list(generator.generate()) classes[Task] += filter(NativeRecipe._is_task, generated_tasks) for task in classes[Task]: task.name = task.name or task.__name__.lower() task.joltdir = self.joltdir or os.path.dirname(self.path) task.joltproject = self.project self.tasks.append(task) log.verbose("Loaded: {0}", self.path)
def _list(ctx, task=None, all=False, reverse=None): """ List all tasks, or dependencies of a task. By default, when no TASK is specified, all known task names are listed in alphabetical order. When a TASK is specified, only direct dependencies of that task are listed. Use -a to also list its indirect dependencies. Multiple TASK names are allowed. """ raise_error_if(not task and reverse, "TASK required with --reverse") registry = TaskRegistry.get() if not task: classes = registry.get_task_classes() for task in sorted(classes, key=lambda x: x.name): if task.name: print(task.name) return task = [utils.stable_task_name(t) for t in task] reverse = [utils.stable_task_name(t) for t in utils.as_list(reverse or [])] try: dag = graph.GraphBuilder(registry, ctx.obj["manifest"]).build(task, influence=False) except JoltError as e: raise e except Exception: raise_error( "an exception occurred during task dependency evaluation, see log for details" ) task = reverse or task nodes = dag.select(lambda graph, node: node.short_qualified_name in task or node.qualified_name in task) nodes = list(nodes) iterator = dag.predecessors if reverse else dag.successors tasklist = set() while nodes: node = nodes.pop() for task in iterator(node): if all and task.short_qualified_name not in tasklist: new_node = dag.get_task(task.qualified_name) nodes.append(new_node) tasklist.add(task.short_qualified_name) for task in sorted(list(tasklist)): print(task)
def new_git(url, path, relpath, refspecs=None): refspecs = utils.as_list(refspecs or []) try: git = _gits[path] raise_error_if(git.url != url, "multiple git repositories required at {}", relpath) raise_error_if( git.refspecs != refspecs, "conflicting refspecs detected for git repository at {}", relpath) return git except Exception: git = _gits[path] = GitRepository(url, path, relpath, refspecs) return git
def _list(ctx, task=None, reverse=None): """ List all tasks, or dependencies of a task. """ raise_error_if(not task and reverse, "TASK required with --reverse") registry = TaskRegistry.get() if not task: classes = registry.get_task_classes() classes += registry.get_test_classes() for task in sorted(classes, key=lambda x: x.name): if task.name: print(task.name) return task = [utils.stable_task_name(t) for t in task] reverse = [utils.stable_task_name(t) for t in utils.as_list(reverse or [])] try: dag = graph.GraphBuilder(registry, ctx.obj["manifest"]).build(task, influence=False) except JoltError as e: raise e except Exception: raise_error( "an exception occurred during task dependency evaluation, see log for details" ) task = reverse or task nodes = dag.select(lambda graph, node: node.short_qualified_name in task or node.qualified_name in task) tasklist = set() iterator = dag.predecessors if reverse else dag.successors for node in nodes: for task in iterator(node): tasklist.add(task.short_qualified_name) for task in sorted(list(tasklist)): print(task)
def load(self): super(NativeRecipe, self).load() name = utils.canonical(self.path) module = imp.load_source("joltfile_{0}".format(name), self.path) classes = inspect.getmoduleclasses(module, [Task, TaskGenerator], NativeRecipe._is_abstract) for cls in classes[TaskGenerator]: cls.joltdir = self.joltdir or os.path.dirname(self.path) generated_tasks = utils.as_list(cls().generate()) classes[Task] += filter(NativeRecipe._is_task, generated_tasks) for task in classes[Task]: task.name = task.name or task.__name__.lower() task.joltdir = self.joltdir or os.path.dirname(self.path) task.joltproject = self.project self.tasks.append(task) log.verbose("Loaded: {0}", self.path)
def inspect(ctx, task, influence=False, artifact=False, salt=None): """ View information about a task. This command displays information about a task, such as its class documentation, parameters and their accepted values, requirements, task class origin (file/line), influence attributes, artifact identity, cache status, and more. Default parameter values, if any, are highlighted. """ task_name = task task_cls_name, task_params = utils.parse_task_name(task_name) task_registry = TaskRegistry.get() task = task_registry.get_task_class(task_cls_name) raise_task_error_if(not task, task_name, "no such task") from jolt import inspection print() print(" {0}".format(task.name)) print() if task.__doc__: print(" {0}".format(task.__doc__.strip())) print() print(" Parameters") has_param = False params = { key: getattr(task, key) for key in dir(task) if isinstance(utils.getattr_safe(task, key), Parameter) } for item, param in params.items(): has_param = True print(" {0:<15} {1}".format(item, param.help or "")) if not has_param: print(" None") print() print(" Definition") print(" {0:<15} {1} ({2})".format( "File", fs.path.relpath(inspection.getfile(task), JoltLoader.get().joltdir), inspection.getlineno(task))) print() print(" Requirements") manifest = ctx.obj["manifest"] try: task = task_registry.get_task(task_name, manifest=manifest) for req in sorted( utils.as_list(utils.call_or_return(task, task.requires))): print(" {0}".format(task.tools.expand(req))) if not task.requires: print(" None") print() except Exception as e: log.exception() if "has not been set" in str(e): print(" Unavailable (parameters must be set)") print() return print(" Unavailable (exception during evaluation)") print() return if salt: task.taint = salt if artifact: acache = cache.ArtifactCache.get() builder = graph.GraphBuilder(task_registry, manifest) dag = builder.build([task.qualified_name]) tasks = dag.select(lambda graph, node: node.task is task) assert len(tasks) == 1, "graph produced multiple tasks, one expected" proxy = tasks[0] task = proxy.task print(" Cache") print(" Identity {0}".format(proxy.identity)) if acache.is_available_locally(proxy): with acache.get_artifact(proxy) as artifact: print(" Location {0}".format(artifact.path)) print(" Local True ({0})".format( utils.as_human_size(acache.get_artifact(proxy).get_size()))) else: print(" Local False") print(" Remote {0}".format( acache.is_available_remotely(proxy))) print() if influence: print(" Influence") for string in HashInfluenceRegistry.get().get_strings(task): string = string.split(":", 1) print(" {:<18}{}".format(string[0][10:], string[1].strip()))
def set_value(self, value, expand=True): values = utils.as_list(value) super(PathEnvironmentVariable, self).set_value(fs.pathsep.join(values), expand)