Example #1
0
  def setUp(self):
    super(EngineTestBase, self).setUp()

    # TODO(John Sirois): Now that the BuildFileParser controls goal registration by iterating
    # over plugin callbacks a PhaseRegistry can be constructed by it and handed to all these
    # callbacks in place of having a global Phase registry.  Remove the Phase static cling.
    Phase.clear()
Example #2
0
 def execute(cls, context, *names):
   parser = OptionParser()
   add_global_options(parser)
   phases = [Phase(name) for name in names]
   Phase.setup_parser(parser, [], phases)
   options, _ = parser.parse_args([])
   context = Context(context.config, options, context.run_tracker, context.target_roots,
                     requested_goals=list(names))
   return cls._execute(context, phases, print_timing=False)
Example #3
0
  def execute(self, targets):
    goal = self.context.options.help_goal
    if goal is None:
      return self.list_goals('You must supply a goal name to provide help for.')
    phase = Phase(goal)
    if not phase.goals():
      self.list_goals('Goal %s is unknown.' % goal)

    parser = setup_parser_for_phase_help(phase)
    parser.parse_args(['--help'])
Example #4
0
  def execute(self, targets):
    goal = self.context.options.help_goal
    if goal is None:
      return self.list_goals('You must supply a goal name to provide help for.')
    phase = Phase(goal)
    if not phase.goals():
      return self.list_goals('Goal %s is unknown.' % goal)

    parser = OptionParser()
    parser.set_usage('%s goal %s ([target]...)' % (sys.argv[0], goal))
    parser.epilog = phase.description
    Goal.add_global_options(parser)
    Phase.setup_parser(parser, [], [phase])
    parser.parse_args(['--help'])
Example #5
0
  def test_load_valid_partial_goals(self):
    def register_goals():
      Phase('jack').install(Goal('jill', lambda: 42))

    with self.create_register(register_goals=register_goals) as backend_package:
      Phase.clear()
      self.assertEqual(0, len(Phase.all()))

      load_backend(self.build_configuration, backend_package)
      self.assert_empty_aliases()
      self.assertEqual(1, len(Phase.all()))

      goals = Phase('jack').goals()
      self.assertEqual(1, len(goals))

      goal = goals[0]
      self.assertEqual('jill', goal.name)
Example #6
0
        def graph():
            def get_cluster_name(phase):
                return 'cluster_%s' % phase.name.replace('-', '_')

            def get_goal_name(phase, goal):
                name = '%s_%s' % (phase.name, goal.name)
                return name.replace('-', '_')

            phase_by_phasename = {}
            for phase, goals in Phase.all():
                phase_by_phasename[phase.name] = phase

            yield '\n'.join([
                'digraph G {',
                '  rankdir=LR;',
                '  graph [compound=true];',
            ])
            for phase, installed_goals in Phase.all():
                yield '\n'.join([
                    '  subgraph %s {' % get_cluster_name(phase),
                    '    node [style=filled];',
                    '    color = blue;',
                    '    label = "%s";' % phase.name,
                ])
                for installed_goal in installed_goals:
                    yield '    %s [label="%s"];' % (get_goal_name(
                        phase, installed_goal), installed_goal.name)
                yield '  }'

            edges = set()
            for phase, installed_goals in Phase.all():
                for installed_goal in installed_goals:
                    for dependency in installed_goal.dependencies:
                        tail_goal = phase_by_phasename.get(
                            dependency.name).goals()[-1]
                        edge = 'ltail=%s lhead=%s' % (get_cluster_name(phase),
                                                      get_cluster_name(
                                                          Phase.of(tail_goal)))
                        if edge not in edges:
                            yield '  %s -> %s [%s];' % (
                                get_goal_name(phase, installed_goal),
                                get_goal_name(Phase.of(tail_goal),
                                              tail_goal), edge)
                        edges.add(edge)
            yield '}'
Example #7
0
    def graph():
      def get_cluster_name(phase):
        return 'cluster_%s' % phase.name.replace('-', '_')

      def get_goal_name(phase, goal):
        name = '%s_%s' % (phase.name, goal.name)
        return name.replace('-', '_')

      phase_by_phasename = {}
      for phase, goals in Phase.all():
        phase_by_phasename[phase.name] = phase

      yield '\n'.join([
        'digraph G {',
        '  rankdir=LR;',
        '  graph [compound=true];',
        ])
      for phase, installed_goals in Phase.all():
        yield '\n'.join([
          '  subgraph %s {' % get_cluster_name(phase),
          '    node [style=filled];',
          '    color = blue;',
          '    label = "%s";' % phase.name,
        ])
        for installed_goal in installed_goals:
          yield '    %s [label="%s"];' % (get_goal_name(phase, installed_goal),
                                          installed_goal.name)
        yield '  }'

      edges = set()
      for phase, installed_goals in Phase.all():
        for installed_goal in installed_goals:
          for dependency in installed_goal.dependencies:
            tail_goal = phase_by_phasename.get(dependency.name).goals()[-1]
            edge = 'ltail=%s lhead=%s' % (get_cluster_name(phase),
                                          get_cluster_name(Phase.of(tail_goal)))
            if edge not in edges:
              yield '  %s -> %s [%s];' % (get_goal_name(phase, installed_goal),
                                          get_goal_name(Phase.of(tail_goal), tail_goal),
                                          edge)
            edges.add(edge)
      yield '}'
Example #8
0
def register_goals():
  changed = Phase('changed').with_description('Print the targets changed since some prior commit.')
  changed.install(Goal(name='changed', action=ScmWhatChanged))

  # We always want compile to finish with a checkstyle
  compile = Phase('compile')
  compile.install(Goal(name='checkstyle', action=Checkstyle,
                       dependencies=['gen', 'resolve']))
Example #9
0
  def _record(self, goal, elapsed):
    phase = Phase.of(goal)

    phase_timings = self._timings.get(phase)
    if phase_timings is None:
      phase_timings = OrderedDict(())
      self._timings[phase] = phase_timings

    goal_timings = phase_timings.get(goal)
    if goal_timings is None:
      goal_timings = []
      phase_timings[goal] = goal_timings

    goal_timings.append(elapsed)
Example #10
0
  def parse_args(args):
    goals = OrderedSet()
    specs = OrderedSet()
    explicit_multi = False
    logger = logging.getLogger(__name__)
    has_double_dash = u'--' in args
    goal_names = [phase.name for phase, goal in Phase.all()]
    if not goal_names:
      raise GoalError(
        'Arguments cannot be parsed before the list of goals from Phase.all() is populated.')

    def is_spec(spec):
      if os.sep in spec or ':' in spec:
        return True # Definitely not a goal.
      if not (spec in goal_names):
        return True # Definitely not a (known) goal.
      if has_double_dash:
        # This means that we're parsing the half of the expression before a --, so assume it's a
        # goal without warning.
        return False
      # Here, it's possible we have a goal and target with the same name. For now, always give
      # priority to the goal, but give a warning if they might have meant the target (if the BUILD
      # file exists).
      try:
        BuildFile(get_buildroot(), spec)
        msg = (' Command-line argument "{spec}" is ambiguous, and was assumed to be a goal.'
               ' If this is incorrect, disambiguate it with the "--" argument to separate goals'
               ' from targets.')
        logger.warning(msg.format(spec=spec))
      except IOError: pass # Awesome, it's unambiguous.
      return False


    for i, arg in enumerate(args):
      if not arg.startswith('-'):
        specs.add(arg) if is_spec(arg) else goals.add(arg)
      elif '--' == arg:
        if specs:
          raise Goal.IntermixedArgumentsError('Cannot intermix targets with goals when using --. '
                                              'Targets should appear on the right')
        explicit_multi = True
        del args[i]
        break

    if explicit_multi:
      specs.update(arg for arg in args[len(goals):] if not arg.startswith('-'))

    return goals, specs
Example #11
0
 def report():
     yield 'Installed goals:'
     documented_rows = []
     undocumented = []
     max_width = 0
     for phase, _ in Phase.all():
         if phase.description:
             documented_rows.append((phase.name, phase.description))
             max_width = max(max_width, len(phase.name))
         elif self.context.options.goal_list_all:
             undocumented.append(phase.name)
     for name, description in documented_rows:
         yield '  %s: %s' % (name.rjust(max_width), description)
     if undocumented:
         yield ''
         yield 'Undocumented goals:'
         yield '  %s' % ' '.join(undocumented)
Example #12
0
 def report():
   yield 'Installed goals:'
   documented_rows = []
   undocumented = []
   max_width = 0
   for phase, _ in Phase.all():
     if phase.description:
       documented_rows.append((phase.name, phase.description))
       max_width = max(max_width, len(phase.name))
     elif self.context.options.goal_list_all:
       undocumented.append(phase.name)
   for name, description in documented_rows:
     yield '  %s: %s' % (name.rjust(max_width), description)
   if undocumented:
     yield ''
     yield 'Undocumented goals:'
     yield '  %s' % ' '.join(undocumented)
Example #13
0
  def setup_parser(self, parser, args):
    self.config = Config.load()
    add_global_options(parser)

    # We support attempting zero or more goals.  Multiple goals must be delimited from further
    # options and non goal args with a '--'.  The key permutations we need to support:
    # ./pants goal => goals
    # ./pants goal goals => goals
    # ./pants goal compile src/java/... => compile
    # ./pants goal compile -x src/java/... => compile
    # ./pants goal compile src/java/... -x => compile
    # ./pants goal compile run -- src/java/... => compile, run
    # ./pants goal compile run -- src/java/... -x => compile, run
    # ./pants goal compile run -- -x src/java/... => compile, run

    if not args:
      args.append('help')

    help_flags = set(['-h', '--help', 'help'])
    show_help = len(help_flags.intersection(args)) > 0
    args = filter(lambda f: f not in help_flags, args)

    goals, specs = Goal.parse_args(args)
    if show_help:
      print_help(goals)
      sys.exit(0)


    self.requested_goals = goals

    with self.run_tracker.new_workunit(name='setup', labels=[WorkUnit.SETUP]):
      # Bootstrap user goals by loading any BUILD files implied by targets.
      spec_parser = SpecParser(self.root_dir, self.build_file_parser)
      with self.run_tracker.new_workunit(name='parse', labels=[WorkUnit.SETUP]):
        for spec in specs:
          for address in spec_parser.parse_addresses(spec):
            self.build_file_parser.inject_spec_closure_into_build_graph(address.spec,
                                                                        self.build_graph)
            self.targets.append(self.build_graph.get_target(address))
    self.phases = [Phase(goal) for goal in goals]

    rcfiles = self.config.getdefault('rcfiles', type=list,
                                     default=['/etc/pantsrc', '~/.pants.rc'])
    if rcfiles:
      rcfile = RcFile(rcfiles, default_prepend=False, process_default=True)

      # Break down the goals specified on the command line to the full set that will be run so we
      # can apply default flags to inner goal nodes.  Also break down goals by Task subclass and
      # register the task class hierarchy fully qualified names so we can apply defaults to
      # baseclasses.

      sections = OrderedSet()
      for phase in Engine.execution_order(self.phases):
        for goal in phase.goals():
          sections.add(goal.name)
          for clazz in goal.task_type.mro():
            if clazz == Task:
              break
            sections.add('%s.%s' % (clazz.__module__, clazz.__name__))

      augmented_args = rcfile.apply_defaults(sections, args)
      if augmented_args != args:
        del args[:]
        args.extend(augmented_args)
        sys.stderr.write("(using pantsrc expansion: pants goal %s)\n" % ' '.join(augmented_args))

    Phase.setup_parser(parser, args, self.phases)
Example #14
0
  def setup_parser(self, parser, args):
    self.config = Config.load()
    add_global_options(parser)

    # We support attempting zero or more goals.  Multiple goals must be delimited from further
    # options and non goal args with a '--'.  The key permutations we need to support:
    # ./pants goal => goals
    # ./pants goal goals => goals
    # ./pants goal compile src/java/... => compile
    # ./pants goal compile -x src/java/... => compile
    # ./pants goal compile src/java/... -x => compile
    # ./pants goal compile run -- src/java/... => compile, run
    # ./pants goal compile run -- src/java/... -x => compile, run
    # ./pants goal compile run -- -x src/java/... => compile, run

    if not args:
      args.append('goals')

    if len(args) == 1 and args[0] in set(['-h', '--help', 'help']):
      def format_usage(usages):
        left_colwidth = 0
        for left, right in usages:
          left_colwidth = max(left_colwidth, len(left))
        lines = []
        for left, right in usages:
          lines.append('  %s%s%s' % (left, ' ' * (left_colwidth - len(left) + 1), right))
        return '\n'.join(lines)

      usages = [
        ("%prog goal goals ([spec]...)", Phase('goals').description),
        ("%prog goal help [goal] ([spec]...)", Phase('help').description),
        ("%prog goal [goal] [spec]...", "Attempt goal against one or more targets."),
        ("%prog goal [goal] ([goal]...) -- [spec]...", "Attempts all the specified goals."),
      ]
      parser.set_usage("\n%s" % format_usage(usages))
      parser.epilog = ("Either lists all installed goals, provides extra help for a goal or else "
                       "attempts to achieve the specified goal for the listed targets.")

      parser.print_help()

      # Add some text that we can't put in the epilog, because that formats away newlines.
      print(textwrap.dedent("""
        Note that target specs accept two special forms:
          [dir]:  to include all targets in the specified directory
          [dir]:: to include all targets found recursively under the directory"""))
      sys.exit(0)
    else:
      goals, specs = Goal.parse_args(args)
      self.requested_goals = goals

      with self.run_tracker.new_workunit(name='setup', labels=[WorkUnit.SETUP]):
        # Bootstrap goals by loading any configured bootstrap BUILD files
        with self.check_errors('The following bootstrap_buildfiles cannot be loaded:') as error:
          with self.run_tracker.new_workunit(name='bootstrap', labels=[WorkUnit.SETUP]):
            for path in self.config.getlist('goals', 'bootstrap_buildfiles', default = []):
              try:
                buildfile = BuildFile(get_buildroot(), os.path.relpath(path, get_buildroot()))
                ParseContext(buildfile).parse()
              except (TypeError, ImportError, TaskError, GoalError):
                error(path, include_traceback=True)
              except (IOError, SyntaxError):
                error(path)
        # Now that we've parsed the bootstrap BUILD files, and know about the SCM system.
        self.run_tracker.run_info.add_scm_info()

        # Bootstrap user goals by loading any BUILD files implied by targets.
        spec_parser = SpecParser(self.root_dir)
        with self.check_errors('The following targets could not be loaded:') as error:
          with self.run_tracker.new_workunit(name='parse', labels=[WorkUnit.SETUP]):
            for spec in specs:
              try:
                for target, address in spec_parser.parse(spec):
                  if target:
                    self.targets.append(target)
                    # Force early BUILD file loading if this target is an alias that expands
                    # to others.
                    unused = list(target.resolve())
                  else:
                    siblings = Target.get_all_addresses(address.buildfile)
                    prompt = 'did you mean' if len(siblings) == 1 else 'maybe you meant one of these'
                    error('%s => %s?:\n    %s' % (address, prompt,
                                                  '\n    '.join(str(a) for a in siblings)))
              except (TypeError, ImportError, TaskError, GoalError):
                error(spec, include_traceback=True)
              except (IOError, SyntaxError, TargetDefinitionException):
                error(spec)

      self.phases = [Phase(goal) for goal in goals]

      rcfiles = self.config.getdefault('rcfiles', type=list,
                                       default=['/etc/pantsrc', '~/.pants.rc'])
      if rcfiles:
        rcfile = RcFile(rcfiles, default_prepend=False, process_default=True)

        # Break down the goals specified on the command line to the full set that will be run so we
        # can apply default flags to inner goal nodes.  Also break down goals by Task subclass and
        # register the task class hierarchy fully qualified names so we can apply defaults to
        # baseclasses.

        sections = OrderedSet()
        for phase in Engine.execution_order(self.phases):
          for goal in phase.goals():
            sections.add(goal.name)
            for clazz in goal.task_type.mro():
              if clazz == Task:
                break
              sections.add('%s.%s' % (clazz.__module__, clazz.__name__))

        augmented_args = rcfile.apply_defaults(sections, args)
        if augmented_args != args:
          del args[:]
          args.extend(augmented_args)
          sys.stderr.write("(using pantsrc expansion: pants goal %s)\n" % ' '.join(augmented_args))

      Phase.setup_parser(parser, args, self.phases)
Example #15
0
 def tearDown(self):
   Phase.clear()
Example #16
0
def register_goals():
  resources = Phase('resources')
  resources.install(Goal(name='args-apt', action=ResourceMapper,
                         dependencies=['compile']))
Example #17
0
  def tearDown(self):
    Phase.clear()

    super(EngineTestBase, self).tearDown()
Example #18
0
 def as_phase(cls, phase_name):
   """Returns a ``Phase`` object of the given name"""
   return Phase(cls._namespace(phase_name))
Example #19
0
    def setup_parser(self, parser, args):
        self.config = Config.load()
        add_global_options(parser)

        # We support attempting zero or more goals.  Multiple goals must be delimited from further
        # options and non goal args with a '--'.  The key permutations we need to support:
        # ./pants goal => goals
        # ./pants goal goals => goals
        # ./pants goal compile src/java/... => compile
        # ./pants goal compile -x src/java/... => compile
        # ./pants goal compile src/java/... -x => compile
        # ./pants goal compile run -- src/java/... => compile, run
        # ./pants goal compile run -- src/java/... -x => compile, run
        # ./pants goal compile run -- -x src/java/... => compile, run

        if not args:
            args.append('help')

        help_flags = set(['-h', '--help', 'help'])
        show_help = len(help_flags.intersection(args)) > 0
        args = filter(lambda f: f not in help_flags, args)

        goals, specs = Goal.parse_args(args)
        if show_help:
            print_help(goals)
            sys.exit(0)

        self.requested_goals = goals

        with self.run_tracker.new_workunit(name='setup',
                                           labels=[WorkUnit.SETUP]):
            # Bootstrap user goals by loading any BUILD files implied by targets.
            spec_parser = SpecParser(self.root_dir, self.build_file_parser)
            with self.run_tracker.new_workunit(name='parse',
                                               labels=[WorkUnit.SETUP]):
                for spec in specs:
                    for address in spec_parser.parse_addresses(spec):
                        self.build_file_parser.inject_spec_closure_into_build_graph(
                            address.spec, self.build_graph)
                        self.targets.append(
                            self.build_graph.get_target(address))
        self.phases = [Phase(goal) for goal in goals]

        rcfiles = self.config.getdefault(
            'rcfiles', type=list, default=['/etc/pantsrc', '~/.pants.rc'])
        if rcfiles:
            rcfile = RcFile(rcfiles,
                            default_prepend=False,
                            process_default=True)

            # Break down the goals specified on the command line to the full set that will be run so we
            # can apply default flags to inner goal nodes.  Also break down goals by Task subclass and
            # register the task class hierarchy fully qualified names so we can apply defaults to
            # baseclasses.

            sections = OrderedSet()
            for phase in Engine.execution_order(self.phases):
                for goal in phase.goals():
                    sections.add(goal.name)
                    for clazz in goal.task_type.mro():
                        if clazz == Task:
                            break
                        sections.add('%s.%s' %
                                     (clazz.__module__, clazz.__name__))

            augmented_args = rcfile.apply_defaults(sections, args)
            if augmented_args != args:
                del args[:]
                args.extend(augmented_args)
                sys.stderr.write("(using pantsrc expansion: pants goal %s)\n" %
                                 ' '.join(augmented_args))

        Phase.setup_parser(parser, args, self.phases)