Ejemplo n.º 1
0
class CmdLineSpecParserBadBuildTest(BaseTest):
    def setUp(self):
        super(CmdLineSpecParserBadBuildTest, self).setUp()

        self.add_to_build_file("bad/a", "a_is_bad")
        self.add_to_build_file("bad/b", "b_is_bad")

        self.spec_parser = CmdLineSpecParser(self.build_root, self.address_mapper)

        self.NO_FAIL_FAST_RE = """^Exception message: name 'a_is_bad' is not defined
 while executing BUILD file (/[^/]+)*/bad/a/BUILD
 Loading addresses from 'bad/a' failed\.
Exception message: name 'b_is_bad' is not defined
 while executing BUILD file (/[^/]+)*T/bad/b/BUILD
 Loading addresses from 'bad/b' failed\.
Invalid BUILD files for \[::\]$"""

        self.FAIL_FAST_RE = """^name 'a_is_bad' is not defined
 while executing BUILD file (/[^/]+)*/bad/a/BUILD
 Loading addresses from 'bad/a' failed.$"""

    def test_bad_build_files(self):
        with self.assertRaisesRegexp(self.spec_parser.BadSpecError, self.NO_FAIL_FAST_RE):
            list(self.spec_parser.parse_addresses("::"))

    def test_bad_build_files_fail_fast(self):
        with self.assertRaisesRegexp(self.spec_parser.BadSpecError, self.FAIL_FAST_RE):
            list(self.spec_parser.parse_addresses("::", True))
Ejemplo n.º 2
0
class CmdLineSpecParserBadBuildTest(BaseTest):
    def setUp(self):
        super(CmdLineSpecParserBadBuildTest, self).setUp()

        self.add_to_build_file('bad/a', 'a_is_bad')
        self.add_to_build_file('bad/b', 'b_is_bad')

        self.spec_parser = CmdLineSpecParser(self.build_root,
                                             self.address_mapper)

        self.NO_FAIL_FAST_RE = re.compile(
            r"""^--------------------
.*
Exception message: name 'a_is_bad' is not defined
 while executing BUILD file (/[^/]+)*/bad/a/BUILD
 Loading addresses from 'bad/a' failed\.
.*
Exception message: name 'b_is_bad' is not defined
 while executing BUILD file (/[^/]+)*T/bad/b/BUILD
 Loading addresses from 'bad/b' failed\.
Invalid BUILD files for \[::\]$""", re.DOTALL)

        self.FAIL_FAST_RE = """^name 'a_is_bad' is not defined
 while executing BUILD file (/[^/]+)*/bad/a/BUILD
 Loading addresses from 'bad/a' failed.$"""

    def test_bad_build_files(self):
        with self.assertRaisesRegexp(self.spec_parser.BadSpecError,
                                     self.NO_FAIL_FAST_RE):
            list(self.spec_parser.parse_addresses('::'))

    def test_bad_build_files_fail_fast(self):
        with self.assertRaisesRegexp(self.spec_parser.BadSpecError,
                                     self.FAIL_FAST_RE):
            list(self.spec_parser.parse_addresses('::', True))
Ejemplo n.º 3
0
class CmdLineSpecParserBadBuildTest(BaseTest):

  def setUp(self):
    super(CmdLineSpecParserBadBuildTest, self).setUp()

    self.add_to_build_file('bad/a', 'a_is_bad')
    self.add_to_build_file('bad/b', 'b_is_bad')

    self.spec_parser = CmdLineSpecParser(self.build_root, self.address_mapper)

    self.NO_FAIL_FAST_RE = re.compile(r"""^--------------------
.*
Exception message: name 'a_is_bad' is not defined
 while executing BUILD file FilesystemBuildFile\((/[^/]+)*/bad/a/BUILD\)
 Loading addresses from 'bad/a' failed\.
.*
Exception message: name 'b_is_bad' is not defined
 while executing BUILD file FilesystemBuildFile\((/[^/]+)*T/bad/b/BUILD\)
 Loading addresses from 'bad/b' failed\.
Invalid BUILD files for \[::\]$""", re.DOTALL)

    self.FAIL_FAST_RE = """^name 'a_is_bad' is not defined
 while executing BUILD file FilesystemBuildFile\((/[^/]+)*/bad/a/BUILD\)
 Loading addresses from 'bad/a' failed.$"""

  def test_bad_build_files(self):
    with self.assertRaisesRegexp(self.spec_parser.BadSpecError, self.NO_FAIL_FAST_RE):
      list(self.spec_parser.parse_addresses('::'))

  def test_bad_build_files_fail_fast(self):
    with self.assertRaisesRegexp(self.spec_parser.BadSpecError, self.FAIL_FAST_RE):
      list(self.spec_parser.parse_addresses('::', True))
Ejemplo n.º 4
0
  def _expand_goals_and_specs(self):
    goals = self.options.goals
    specs = self.options.target_specs
    fail_fast = self.options.for_global_scope().fail_fast

    for goal in goals:
      if self.address_mapper.from_cache(get_buildroot(), goal, must_exist=False).file_exists():
        logger.warning(" Command-line argument '{0}' is ambiguous and was assumed to be "
                       "a goal. If this is incorrect, disambiguate it with ./{0}.".format(goal))

    if self.options.help_request:
      help_printer = HelpPrinter(self.options)
      help_printer.print_help()
      self._exiter(0)

    self.requested_goals = goals

    with self.run_tracker.new_workunit(name='setup', labels=[WorkUnitLabel.SETUP]):
      spec_parser = CmdLineSpecParser(self.root_dir, self.address_mapper,
                                      spec_excludes=self.spec_excludes,
                                      exclude_target_regexps=self.global_options.exclude_target_regexp)
      with self.run_tracker.new_workunit(name='parse', labels=[WorkUnitLabel.SETUP]):
        def filter_for_tag(tag):
          return lambda target: tag in map(str, target.tags)
        tag_filter = wrap_filters(create_filters(self.global_options.tag, filter_for_tag))
        for spec in specs:
          for address in spec_parser.parse_addresses(spec, fail_fast):
            self.build_graph.inject_address_closure(address)
            tgt = self.build_graph.get_target(address)
            if tag_filter(tgt):
              self.targets.append(tgt)
    self.goals = [Goal.by_name(goal) for goal in goals]
Ejemplo n.º 5
0
    def _expand_goals_and_specs(self):
        goals = self.options.goals
        specs = self.options.target_specs
        fail_fast = self.options.for_global_scope().fail_fast

        for goal in goals:
            if BuildFile.from_cache(get_buildroot(), goal,
                                    must_exist=False).exists():
                logger.warning(
                    " Command-line argument '{0}' is ambiguous and was assumed to be "
                    "a goal. If this is incorrect, disambiguate it with ./{0}."
                    .format(goal))

        if self.options.print_help_if_requested():
            sys.exit(0)

        self.requested_goals = goals

        with self.run_tracker.new_workunit(name='setup',
                                           labels=[WorkUnit.SETUP]):
            spec_parser = CmdLineSpecParser(
                self.root_dir,
                self.address_mapper,
                spec_excludes=self.spec_excludes,
                exclude_target_regexps=self.global_options.
                exclude_target_regexp)
            with self.run_tracker.new_workunit(name='parse',
                                               labels=[WorkUnit.SETUP]):
                for spec in specs:
                    for address in spec_parser.parse_addresses(
                            spec, fail_fast):
                        self.build_graph.inject_address_closure(address)
                        self.targets.append(
                            self.build_graph.get_target(address))
        self.goals = [Goal.by_name(goal) for goal in goals]
Ejemplo n.º 6
0
  def _expand_goals_and_specs(self):
    goals = self.options.goals
    specs = self.options.target_specs
    fail_fast = self.options.for_global_scope().fail_fast

    for goal in goals:
      if BuildFile.from_cache(get_buildroot(), goal, must_exist=False).exists():
        logger.warning(" Command-line argument '{0}' is ambiguous and was assumed to be "
                       "a goal. If this is incorrect, disambiguate it with ./{0}.".format(goal))

    if self.options.print_help_if_requested():
      sys.exit(0)

    self.requested_goals = goals

    with self.run_tracker.new_workunit(name='setup', labels=[WorkUnit.SETUP]):
      spec_parser = CmdLineSpecParser(self.root_dir, self.address_mapper,
                                      spec_excludes=self.spec_excludes,
                                      exclude_target_regexps=self.global_options.exclude_target_regexp)
      with self.run_tracker.new_workunit(name='parse', labels=[WorkUnit.SETUP]):
        for spec in specs:
          for address in spec_parser.parse_addresses(spec, fail_fast):
            self.build_graph.inject_address_closure(address)
            self.targets.append(self.build_graph.get_target(address))
    self.goals = [Goal.by_name(goal) for goal in goals]
Ejemplo n.º 7
0
  def setup_parser(self, parser, args):
    if not args:
      args.append('help')

    logger = logging.getLogger(__name__)

    goals = self.new_options.goals
    specs = self.new_options.target_specs
    fail_fast = self.new_options.for_global_scope().fail_fast

    for goal in goals:
      if BuildFile.from_cache(get_buildroot(), goal, must_exist=False).exists():
        logger.warning(" Command-line argument '{0}' is ambiguous and was assumed to be "
                       "a goal. If this is incorrect, disambiguate it with ./{0}.".format(goal))

    if self.new_options.is_help:
      self.new_options.print_help(goals=goals)
      sys.exit(0)

    self.requested_goals = goals

    with self.run_tracker.new_workunit(name='setup', labels=[WorkUnit.SETUP]):
      spec_parser = CmdLineSpecParser(self.root_dir, self.address_mapper,
                                      spec_excludes=self.get_spec_excludes())
      with self.run_tracker.new_workunit(name='parse', labels=[WorkUnit.SETUP]):
        for spec in specs:
          for address in spec_parser.parse_addresses(spec, fail_fast):
            self.build_graph.inject_address_closure(address)
            self.targets.append(self.build_graph.get_target(address))
    self.goals = [Goal.by_name(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 goal in Engine.execution_order(self.goals):
        for task_name in goal.ordered_task_names():
          sections.add(task_name)
          task_type = goal.task_type_by_name(task_name)
          for clazz in 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:
        # TODO(John Sirois): Cleanup this currently important mutation of the passed in args
        # once the 2-layer of command -> goal is squashed into one.
        args[:] = augmented_args
        sys.stderr.write("(using pantsrc expansion: pants goal %s)\n" % ' '.join(augmented_args))
Ejemplo n.º 8
0
 def _get_targets(spec):
   try:
     spec_parser = CmdLineSpecParser(get_buildroot(), self.context.build_file_parser)
     addresses = spec_parser.parse_addresses(spec)
   except (IOError, ValueError) as e:
     raise TaskError('Failed to parse spec: %s: %s' % (spec, e))
   matches = set(self.context.build_graph.get_target(address) for address in addresses)
   if not matches:
     raise TaskError('No matches for spec: %s' % spec)
   return matches
Ejemplo n.º 9
0
    def __init__(self, *args, **kwargs):
        super(Build, self).__init__(*args, **kwargs)

        if not self.args:
            self.error("A spec argument is required")

        self.config = Config.load()

        interpreters = self.options.interpreters or [b'']
        self.interpreter_cache = PythonInterpreterCache(self.config,
                                                        logger=self.debug)
        self.interpreter_cache.setup(filters=interpreters)
        interpreters = self.interpreter_cache.select_interpreter(
            list(self.interpreter_cache.matches(interpreters)))
        if len(interpreters) != 1:
            self.error('Unable to detect suitable interpreter.')
        else:
            self.debug('Selected %s' % interpreters[0])
        self.interpreter = interpreters[0]

        try:
            specs_end = self.args.index('--')
            if len(self.args) > specs_end:
                self.build_args = self.args[specs_end + 1:len(self.args) + 1]
            else:
                self.build_args = []
        except ValueError:
            specs_end = 1
            self.build_args = self.args[1:] if len(self.args) > 1 else []

        self.targets = OrderedSet()
        spec_parser = CmdLineSpecParser(self.root_dir, self.address_mapper)
        self.top_level_addresses = set()

        specs = self.args[0:specs_end]
        addresses = spec_parser.parse_addresses(specs)

        for address in addresses:
            self.top_level_addresses.add(address)
            try:
                self.build_graph.inject_address_closure(address)
                target = self.build_graph.get_target(address)
            except:
                self.error("Problem parsing BUILD target %s: %s" %
                           (address, traceback.format_exc()))

            if not target:
                self.error("Target %s does not exist" % address)

            transitive_targets = self.build_graph.transitive_subgraph_of_addresses(
                [target.address])
            for transitive_target in transitive_targets:
                self.targets.add(transitive_target)

        self.targets = [target for target in self.targets if target.is_python]
Ejemplo n.º 10
0
  def __init__(self, *args, **kwargs):
    super(Build, self).__init__(*args, **kwargs)

    if not self.args:
      self.error("A spec argument is required")

    self._verbose = self.options.verbose

    self.config = Config.load()

    interpreters = self.options.interpreters or [b'']
    self.interpreter_cache = PythonInterpreterCache(self.config, logger=self.debug)
    self.interpreter_cache.setup(filters=interpreters)
    interpreters = self.interpreter_cache.select_interpreter(
        list(self.interpreter_cache.matches(interpreters)))
    if len(interpreters) != 1:
      self.error('Unable to detect suitable interpreter.')
    else:
      self.debug('Selected %s' % interpreters[0])
    self.interpreter = interpreters[0]

    try:
      specs_end = self.args.index('--')
      if len(self.args) > specs_end:
        self.build_args = self.args[specs_end+1:len(self.args)+1]
      else:
        self.build_args = []
    except ValueError:
      specs_end = 1
      self.build_args = self.args[1:] if len(self.args) > 1 else []

    self.targets = OrderedSet()
    spec_parser = CmdLineSpecParser(self.root_dir, self.address_mapper)
    self.top_level_addresses = set()

    specs = self.args[0:specs_end]
    addresses = spec_parser.parse_addresses(specs)

    for address in addresses:
      self.top_level_addresses.add(address)
      try:
        self.build_graph.inject_address_closure(address)
        target = self.build_graph.get_target(address)
      except:
        self.error("Problem parsing BUILD target %s: %s" % (address, traceback.format_exc()))

      if not target:
        self.error("Target %s does not exist" % address)

      transitive_targets = self.build_graph.transitive_subgraph_of_addresses([target.address])
      for transitive_target in transitive_targets:
        self.targets.add(transitive_target)

    self.targets = [target for target in self.targets if target.is_python]
Ejemplo n.º 11
0
  def targets(self, spec):
    """Resolves a target spec to one or more Target objects.

    spec: Either BUILD target address or else a target glob using the siblings ':' or
          descendants '::' suffixes.

    Returns the set of all Targets found.
    """
    spec_parser = CmdLineSpecParser(self.build_root, self.build_file_parser)
    addresses = list(spec_parser.parse_addresses(spec))
    for address in addresses:
      self.build_file_parser.inject_spec_closure_into_build_graph(address.spec, self.build_graph)
    targets = [self.build_graph.get_target(address) for address in addresses]
    return targets
Ejemplo n.º 12
0
 def _get_targets(spec):
   try:
     spec_parser = CmdLineSpecParser(get_buildroot(), self.context.build_file_parser)
     addresses = spec_parser.parse_addresses(spec)
   except (IOError, ValueError) as e:
     raise TaskError('Failed to parse spec: %s: %s' % (spec, e))
   # filter specs may not have been parsed as part of the context: force parsing
   matches = set()
   for address in addresses:
     self.context.build_file_parser.inject_address_into_build_graph(address, self.context.build_graph)
     matches.add(self.context.build_graph.get_target(address))
   if not matches:
     raise TaskError('No matches for spec: %s' % spec)
   return matches
Ejemplo n.º 13
0
 def _get_targets(spec):
   try:
     spec_parser = CmdLineSpecParser(get_buildroot(), self.context.address_mapper)
     addresses = spec_parser.parse_addresses(spec)
   except AddressLookupError as e:
     raise TaskError('Failed to parse address selector: {spec}\n {message}'.format(spec=spec, message=e))
   # filter specs may not have been parsed as part of the context: force parsing
   matches = set()
   for address in addresses:
     self.context.build_graph.inject_address_closure(address)
     matches.add(self.context.build_graph.get_target(address))
   if not matches:
     raise TaskError('No matches for address selector: {spec}'.format(spec=spec))
   return matches
Ejemplo n.º 14
0
 def _get_targets(spec):
   try:
     spec_parser = CmdLineSpecParser(get_buildroot(), self.context.address_mapper)
     addresses = spec_parser.parse_addresses(spec)
   except AddressLookupError as e:
     raise TaskError('Failed to parse address selector: {spec}\n {message}'.format(spec=spec, message=e))
   # filter specs may not have been parsed as part of the context: force parsing
   matches = set()
   for address in addresses:
     self.context.build_graph.inject_address_closure(address)
     matches.add(self.context.build_graph.get_target(address))
   if not matches:
     raise TaskError('No matches for address selector: {spec}'.format(spec=spec))
   return matches
Ejemplo n.º 15
0
  def targets(self, spec):
    """Resolves a target spec to one or more Target objects.

    spec: Either BUILD target address or else a target glob using the siblings ':' or
          descendants '::' suffixes.

    Returns the set of all Targets found.
    """

    spec_parser = CmdLineSpecParser(self.build_root, self.address_mapper)
    addresses = list(spec_parser.parse_addresses(spec))
    for address in addresses:
      self.build_graph.inject_address_closure(address)
    targets = [self.build_graph.get_target(address) for address in addresses]
    return targets
Ejemplo n.º 16
0
class GoalRunnerFactory(object):
    def __init__(self, root_dir, options, build_config, run_tracker, reporting, exiter=sys.exit):
        """
    :param str root_dir: The root directory of the pants workspace (aka the "build root").
    :param Options options: The global, pre-initialized Options instance.
    :param BuildConfiguration build_config: A pre-initialized BuildConfiguration instance.
    :param Runtracker run_tracker: The global, pre-initialized/running RunTracker instance.
    :param Reporting reporting: The global, pre-initialized Reporting instance.
    :param func exiter: A function that accepts an exit code value and exits (for tests, Optional).
    """
        self._root_dir = root_dir
        self._options = options
        self._build_config = build_config
        self._run_tracker = run_tracker
        self._reporting = reporting
        self._exiter = exiter

        self._goals = []
        self._targets = []
        self._requested_goals = self._options.goals
        self._target_specs = self._options.target_specs
        self._help_request = self._options.help_request

        self._global_options = options.for_global_scope()
        self._tag = self._global_options.tag
        self._fail_fast = self._global_options.fail_fast
        self._spec_excludes = self._global_options.spec_excludes
        self._explain = self._global_options.explain
        self._kill_nailguns = self._global_options.kill_nailguns

        self._project_tree = self._get_project_tree(self._global_options.build_file_rev)
        self._build_file_parser = BuildFileParser(self._build_config, self._root_dir)
        self._address_mapper = BuildFileAddressMapper(self._build_file_parser, self._project_tree)
        self._build_graph = BuildGraph(self._address_mapper)
        self._spec_parser = CmdLineSpecParser(
            self._root_dir,
            self._address_mapper,
            spec_excludes=self._spec_excludes,
            exclude_target_regexps=self._global_options.exclude_target_regexp,
        )

    def _get_project_tree(self, build_file_rev):
        """Creates the project tree for build files for use in a given pants run."""
        if build_file_rev:
            return ScmProjectTree(self._root_dir, get_scm(), build_file_rev)
        else:
            return FileSystemProjectTree(self._root_dir)

    def _expand_goals(self, goals):
        """Check and populate the requested goals for a given run."""
        for goal in goals:
            try:
                self._address_mapper.resolve_spec(goal)
                logger.warning(
                    "Command-line argument '{0}' is ambiguous and was assumed to be "
                    "a goal. If this is incorrect, disambiguate it with ./{0}.".format(goal)
                )
            except AddressLookupError:
                pass

        if self._help_request:
            help_printer = HelpPrinter(self._options)
            result = help_printer.print_help()
            self._exiter(result)

        self._goals.extend([Goal.by_name(goal) for goal in goals])

    def _expand_specs(self, specs, fail_fast):
        """Populate the BuildGraph and target list from a set of input specs."""
        with self._run_tracker.new_workunit(name="parse", labels=[WorkUnitLabel.SETUP]):

            def filter_for_tag(tag):
                return lambda target: tag in map(str, target.tags)

            tag_filter = wrap_filters(create_filters(self._tag, filter_for_tag))

            for spec in specs:
                for address in self._spec_parser.parse_addresses(spec, fail_fast):
                    self._build_graph.inject_address_closure(address)
                    target = self._build_graph.get_target(address)
                    if tag_filter(target):
                        self._targets.append(target)

    def _maybe_launch_pantsd(self):
        """Launches pantsd if configured to do so."""
        if self._global_options.enable_pantsd:
            # Avoid runtracker output if pantsd is disabled. Otherwise, show up to inform the user its on.
            with self._run_tracker.new_workunit(name="pantsd", labels=[WorkUnitLabel.SETUP]):
                PantsDaemonLauncher.global_instance().maybe_launch()

    def _is_quiet(self):
        return any(goal.has_task_of_type(QuietTaskMixin) for goal in self._goals) or self._explain

    def _setup_context(self):
        self._maybe_launch_pantsd()

        with self._run_tracker.new_workunit(name="setup", labels=[WorkUnitLabel.SETUP]):
            self._expand_goals(self._requested_goals)
            self._expand_specs(self._target_specs, self._fail_fast)

            # Now that we've parsed the bootstrap BUILD files, and know about the SCM system.
            self._run_tracker.run_info.add_scm_info()

            # Update the Reporting settings now that we have options and goal info.
            invalidation_report = self._reporting.update_reporting(
                self._global_options, self._is_quiet(), self._run_tracker
            )

            context = Context(
                options=self._options,
                run_tracker=self._run_tracker,
                target_roots=self._targets,
                requested_goals=self._requested_goals,
                build_graph=self._build_graph,
                build_file_parser=self._build_file_parser,
                address_mapper=self._address_mapper,
                spec_excludes=self._spec_excludes,
                invalidation_report=invalidation_report,
            )

        return context, invalidation_report

    def setup(self):
        context, invalidation_report = self._setup_context()
        return GoalRunner(
            context=context,
            goals=self._goals,
            kill_nailguns=self._kill_nailguns,
            run_tracker=self._run_tracker,
            invalidation_report=invalidation_report,
            exiter=self._exiter,
        )
Ejemplo n.º 17
0
class CmdLineSpecParserTest(BaseTest):
    @property
    def alias_groups(self):
        return BuildFileAliases.create(targets={"generic": Target})

    def setUp(self):
        super(CmdLineSpecParserTest, self).setUp()

        def add_target(path, name):
            self.add_to_build_file(path, 'generic(name="{name}")\n'.format(name=name))

        add_target("BUILD", "root")
        add_target("a", "a")
        add_target("a", "b")
        add_target("a/b", "b")
        add_target("a/b", "c")

        self.spec_parser = CmdLineSpecParser(self.build_root, self.address_mapper)

    def test_normal(self):
        self.assert_parsed(cmdline_spec=":root", expected=[":root"])
        self.assert_parsed(cmdline_spec="//:root", expected=[":root"])

        self.assert_parsed(cmdline_spec="a", expected=["a"])
        self.assert_parsed(cmdline_spec="a:a", expected=["a"])

        self.assert_parsed(cmdline_spec="a/b", expected=["a/b"])
        self.assert_parsed(cmdline_spec="a/b:b", expected=["a/b"])
        self.assert_parsed(cmdline_spec="a/b:c", expected=["a/b:c"])

    def test_sibling(self):
        self.assert_parsed(cmdline_spec=":", expected=[":root"])
        self.assert_parsed(cmdline_spec="//:", expected=[":root"])

        self.assert_parsed(cmdline_spec="a:", expected=["a", "a:b"])
        self.assert_parsed(cmdline_spec="//a:", expected=["a", "a:b"])

        self.assert_parsed(cmdline_spec="a/b:", expected=["a/b", "a/b:c"])
        self.assert_parsed(cmdline_spec="//a/b:", expected=["a/b", "a/b:c"])

    def test_sibling_or_descendents(self):
        self.assert_parsed(cmdline_spec="::", expected=[":root", "a", "a:b", "a/b", "a/b:c"])
        self.assert_parsed(cmdline_spec="//::", expected=[":root", "a", "a:b", "a/b", "a/b:c"])

        self.assert_parsed(cmdline_spec="a::", expected=["a", "a:b", "a/b", "a/b:c"])
        self.assert_parsed(cmdline_spec="//a::", expected=["a", "a:b", "a/b", "a/b:c"])

        self.assert_parsed(cmdline_spec="a/b::", expected=["a/b", "a/b:c"])
        self.assert_parsed(cmdline_spec="//a/b::", expected=["a/b", "a/b:c"])

    def test_absolute(self):
        self.assert_parsed(cmdline_spec=os.path.join(self.build_root, "a"), expected=["a"])
        self.assert_parsed(cmdline_spec=os.path.join(self.build_root, "a:a"), expected=["a"])
        self.assert_parsed(cmdline_spec=os.path.join(self.build_root, "a:"), expected=["a", "a:b"])
        self.assert_parsed(cmdline_spec=os.path.join(self.build_root, "a::"), expected=["a", "a:b", "a/b", "a/b:c"])

        double_absolute = "/" + os.path.join(self.build_root, "a")
        self.assertEquals("//", double_absolute[:2], "A sanity check we have a leading-// absolute spec")
        with self.assertRaises(self.spec_parser.BadSpecError):
            self.spec_parser.parse_addresses(double_absolute).next()

        with self.assertRaises(self.spec_parser.BadSpecError):
            self.spec_parser.parse_addresses("/not/the/buildroot/a").next()

    def test_cmd_line_affordances(self):
        self.assert_parsed(cmdline_spec="./:root", expected=[":root"])
        self.assert_parsed(cmdline_spec="//./:root", expected=[":root"])
        self.assert_parsed(cmdline_spec="//./a/../:root", expected=[":root"])
        self.assert_parsed(cmdline_spec=os.path.join(self.build_root, "./a/../:root"), expected=[":root"])

        self.assert_parsed(cmdline_spec="a/", expected=["a"])
        self.assert_parsed(cmdline_spec="./a/", expected=["a"])
        self.assert_parsed(cmdline_spec=os.path.join(self.build_root, "./a/"), expected=["a"])

        self.assert_parsed(cmdline_spec="a/b/:b", expected=["a/b"])
        self.assert_parsed(cmdline_spec="./a/b/:b", expected=["a/b"])
        self.assert_parsed(cmdline_spec=os.path.join(self.build_root, "./a/b/:b"), expected=["a/b"])

    def test_cmd_line_spec_list(self):
        self.assert_parsed_list(cmdline_spec_list=["a", "a/b"], expected=["a", "a/b"])
        self.assert_parsed_list(cmdline_spec_list=["a", "a/b", "^a/b"], expected=["a"])
        self.assert_parsed_list(cmdline_spec_list=["^a/b", "a", "a/b"], expected=["a"])
        self.assert_parsed_list(cmdline_spec_list=["::"], expected=[":root", "a", "a:b", "a/b", "a/b:c"])
        self.assert_parsed_list(cmdline_spec_list=["::", "^a/b::"], expected=[":root", "a", "a:b"])
        self.assert_parsed_list(cmdline_spec_list=["^a/b::", "::"], expected=[":root", "a", "a:b"])

    def test_does_not_exist(self):
        with self.assertRaises(self.spec_parser.BadSpecError):
            self.spec_parser.parse_addresses("c").next()

        with self.assertRaises(self.spec_parser.BadSpecError):
            self.spec_parser.parse_addresses("c:").next()

        with self.assertRaises(self.spec_parser.BadSpecError):
            self.spec_parser.parse_addresses("c::").next()

    def assert_parsed(self, cmdline_spec, expected):
        def sort(addresses):
            return sorted(addresses, key=lambda address: address.spec)

        self.assertEqual(
            sort(SyntheticAddress.parse(addr) for addr in expected),
            sort(self.spec_parser.parse_addresses(cmdline_spec)),
        )

    def assert_parsed_list(self, cmdline_spec_list, expected):
        def sort(addresses):
            return sorted(addresses, key=lambda address: address.spec)

        self.assertEqual(
            sort(SyntheticAddress.parse(addr) for addr in expected),
            sort(self.spec_parser.parse_addresses(cmdline_spec_list)),
        )
Ejemplo n.º 18
0
class GoalRunnerFactory(object):
    def __init__(self,
                 root_dir,
                 options,
                 build_config,
                 run_tracker,
                 reporting,
                 exiter=sys.exit):
        """
    :param str root_dir: The root directory of the pants workspace (aka the "build root").
    :param Options options: The global, pre-initialized Options instance.
    :param BuildConfiguration build_config: A pre-initialized BuildConfiguration instance.
    :param Runtracker run_tracker: The global, pre-initialized/running RunTracker instance.
    :param Reporting reporting: The global, pre-initialized Reporting instance.
    :param func exiter: A function that accepts an exit code value and exits (for tests, Optional).
    """
        self._root_dir = root_dir
        self._options = options
        self._build_config = build_config
        self._run_tracker = run_tracker
        self._reporting = reporting
        self._exiter = exiter

        self._goals = []
        self._targets = []
        self._requested_goals = self._options.goals
        self._target_specs = self._options.target_specs
        self._help_request = self._options.help_request

        self._global_options = options.for_global_scope()
        self._tag = self._global_options.tag
        self._fail_fast = self._global_options.fail_fast
        self._spec_excludes = self._global_options.spec_excludes
        self._explain = self._global_options.explain
        self._kill_nailguns = self._global_options.kill_nailguns

        self._project_tree = self._get_project_tree(
            self._global_options.build_file_rev)
        self._build_file_parser = BuildFileParser(self._build_config,
                                                  self._root_dir)
        self._address_mapper = BuildFileAddressMapper(self._build_file_parser,
                                                      self._project_tree)
        self._build_graph = BuildGraph(self._address_mapper)
        self._spec_parser = CmdLineSpecParser(
            self._root_dir,
            self._address_mapper,
            spec_excludes=self._spec_excludes,
            exclude_target_regexps=self._global_options.exclude_target_regexp)

    def _get_project_tree(self, build_file_rev):
        """Creates the project tree for build files for use in a given pants run."""
        if build_file_rev:
            return ScmProjectTree(self._root_dir, get_scm(), build_file_rev)
        else:
            return FileSystemProjectTree(self._root_dir)

    def _expand_goals(self, goals):
        """Check and populate the requested goals for a given run."""
        for goal in goals:
            try:
                self._address_mapper.resolve_spec(goal)
                logger.warning(
                    "Command-line argument '{0}' is ambiguous and was assumed to be "
                    "a goal. If this is incorrect, disambiguate it with ./{0}."
                    .format(goal))
            except AddressLookupError:
                pass

        if self._help_request:
            help_printer = HelpPrinter(self._options)
            result = help_printer.print_help()
            self._exiter(result)

        self._goals.extend([Goal.by_name(goal) for goal in goals])

    def _expand_specs(self, specs, fail_fast):
        """Populate the BuildGraph and target list from a set of input specs."""
        with self._run_tracker.new_workunit(name='parse',
                                            labels=[WorkUnitLabel.SETUP]):

            def filter_for_tag(tag):
                return lambda target: tag in map(str, target.tags)

            tag_filter = wrap_filters(create_filters(self._tag,
                                                     filter_for_tag))

            for spec in specs:
                for address in self._spec_parser.parse_addresses(
                        spec, fail_fast):
                    self._build_graph.inject_address_closure(address)
                    target = self._build_graph.get_target(address)
                    if tag_filter(target):
                        self._targets.append(target)

    def _maybe_launch_pantsd(self):
        """Launches pantsd if configured to do so."""
        if self._global_options.enable_pantsd:
            # Avoid runtracker output if pantsd is disabled. Otherwise, show up to inform the user its on.
            with self._run_tracker.new_workunit(name='pantsd',
                                                labels=[WorkUnitLabel.SETUP]):
                PantsDaemonLauncher.global_instance().maybe_launch()

    def _is_quiet(self):
        return any(
            goal.has_task_of_type(QuietTaskMixin)
            for goal in self._goals) or self._explain

    def _setup_context(self):
        self._maybe_launch_pantsd()

        with self._run_tracker.new_workunit(name='setup',
                                            labels=[WorkUnitLabel.SETUP]):
            self._expand_goals(self._requested_goals)
            self._expand_specs(self._target_specs, self._fail_fast)

            # Now that we've parsed the bootstrap BUILD files, and know about the SCM system.
            self._run_tracker.run_info.add_scm_info()

            # Update the Reporting settings now that we have options and goal info.
            invalidation_report = self._reporting.update_reporting(
                self._global_options, self._is_quiet(), self._run_tracker)

            context = Context(options=self._options,
                              run_tracker=self._run_tracker,
                              target_roots=self._targets,
                              requested_goals=self._requested_goals,
                              build_graph=self._build_graph,
                              build_file_parser=self._build_file_parser,
                              address_mapper=self._address_mapper,
                              spec_excludes=self._spec_excludes,
                              invalidation_report=invalidation_report)

        return context, invalidation_report

    def setup(self):
        context, invalidation_report = self._setup_context()
        return GoalRunner(context=context,
                          goals=self._goals,
                          kill_nailguns=self._kill_nailguns,
                          run_tracker=self._run_tracker,
                          invalidation_report=invalidation_report,
                          exiter=self._exiter)
Ejemplo n.º 19
0
class GoalRunnerFactory(object):
    def __init__(self,
                 root_dir,
                 options,
                 build_config,
                 run_tracker,
                 reporting,
                 exiter=sys.exit):
        """
    :param str root_dir: The root directory of the pants workspace (aka the "build root").
    :param Options options: The global, pre-initialized Options instance.
    :param BuildConfiguration build_config: A pre-initialized BuildConfiguration instance.
    :param Runtracker run_tracker: The global, pre-initialized/running RunTracker instance.
    :param Reporting reporting: The global, pre-initialized Reporting instance.
    :param func exiter: A function that accepts an exit code value and exits (for tests, Optional).
    """
        self._root_dir = root_dir
        self._options = options
        self._build_config = build_config
        self._run_tracker = run_tracker
        self._reporting = reporting
        self._exiter = exiter

        self._goals = []
        self._targets = []
        self._requested_goals = self._options.goals
        self._target_specs = self._options.target_specs
        self._help_request = self._options.help_request

        self._global_options = options.for_global_scope()
        self._tag = self._global_options.tag
        self._fail_fast = self._global_options.fail_fast
        self._spec_excludes = self._global_options.spec_excludes
        self._explain = self._global_options.explain
        self._kill_nailguns = self._global_options.kill_nailguns

        self._build_file_type = self._get_buildfile_type(
            self._global_options.build_file_rev)
        self._build_file_parser = BuildFileParser(self._build_config,
                                                  self._root_dir)
        self._address_mapper = BuildFileAddressMapper(self._build_file_parser,
                                                      self._build_file_type)
        self._build_graph = BuildGraph(self._address_mapper)
        self._spec_parser = CmdLineSpecParser(
            self._root_dir,
            self._address_mapper,
            spec_excludes=self._spec_excludes,
            exclude_target_regexps=self._global_options.exclude_target_regexp)

    def _get_buildfile_type(self, build_file_rev):
        """Selects the BuildFile type for use in a given pants run."""
        if build_file_rev:
            ScmBuildFile.set_rev(build_file_rev)
            ScmBuildFile.set_scm(get_scm())
            return ScmBuildFile
        else:
            return FilesystemBuildFile

    def _expand_goals(self, goals):
        """Check and populate the requested goals for a given run."""
        for goal in goals:
            if self._address_mapper.from_cache(self._root_dir,
                                               goal,
                                               must_exist=False).file_exists():
                logger.warning(
                    "Command-line argument '{0}' is ambiguous and was assumed to be "
                    "a goal. If this is incorrect, disambiguate it with ./{0}."
                    .format(goal))

        if self._help_request:
            help_printer = HelpPrinter(self._options)
            help_printer.print_help()
            self._exiter(0)

        self._goals.extend([Goal.by_name(goal) for goal in goals])

    def _expand_specs(self, specs, fail_fast):
        """Populate the BuildGraph and target list from a set of input specs."""
        with self._run_tracker.new_workunit(name='parse',
                                            labels=[WorkUnitLabel.SETUP]):

            def filter_for_tag(tag):
                return lambda target: tag in map(str, target.tags)

            tag_filter = wrap_filters(create_filters(self._tag,
                                                     filter_for_tag))

            for spec in specs:
                for address in self._spec_parser.parse_addresses(
                        spec, fail_fast):
                    self._build_graph.inject_address_closure(address)
                    target = self._build_graph.get_target(address)
                    if tag_filter(target):
                        self._targets.append(target)

    def _is_quiet(self):
        return any(
            goal.has_task_of_type(QuietTaskMixin)
            for goal in self._goals) or self._explain

    def _setup_context(self):
        # TODO(John Sirois): Kill when source root registration is lifted out of BUILD files.
        with self._run_tracker.new_workunit(name='bootstrap',
                                            labels=[WorkUnitLabel.SETUP]):
            source_root_bootstrapper = SourceRootBootstrapper.global_instance()
            source_root_bootstrapper.bootstrap(self._address_mapper,
                                               self._build_file_parser)

        with self._run_tracker.new_workunit(name='setup',
                                            labels=[WorkUnitLabel.SETUP]):
            self._expand_goals(self._requested_goals)
            self._expand_specs(self._target_specs, self._fail_fast)

            # Now that we've parsed the bootstrap BUILD files, and know about the SCM system.
            self._run_tracker.run_info.add_scm_info()

            # Update the Reporting settings now that we have options and goal info.
            invalidation_report = self._reporting.update_reporting(
                self._global_options, self._is_quiet(), self._run_tracker)

            context = Context(options=self._options,
                              run_tracker=self._run_tracker,
                              target_roots=self._targets,
                              requested_goals=self._requested_goals,
                              build_graph=self._build_graph,
                              build_file_parser=self._build_file_parser,
                              address_mapper=self._address_mapper,
                              spec_excludes=self._spec_excludes,
                              invalidation_report=invalidation_report)

        return context, invalidation_report

    def setup(self):
        context, invalidation_report = self._setup_context()
        return GoalRunner(context=context,
                          goals=self._goals,
                          kill_nailguns=self._kill_nailguns,
                          run_tracker=self._run_tracker,
                          invalidation_report=invalidation_report)
Ejemplo n.º 20
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
        non_help_args = filter(lambda f: f not in help_flags, args)

        goals, specs = GoalRunner.parse_args(non_help_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 = CmdLineSpecParser(self.root_dir, self.address_mapper)
            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_graph.inject_address_closure(address)
                        self.targets.append(
                            self.build_graph.get_target(address))
        self.goals = [Goal.by_name(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 goal in Engine.execution_order(self.goals):
                for task_name in goal.ordered_task_names():
                    sections.add(task_name)
                    task_type = goal.task_type_by_name(task_name)
                    for clazz in 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:
                # TODO(John Sirois): Cleanup this currently important mutation of the passed in args
                # once the 2-layer of command -> goal is squashed into one.
                del args[:]
                args.extend(augmented_args)
                sys.stderr.write("(using pantsrc expansion: pants goal %s)\n" %
                                 ' '.join(augmented_args))

        Goal.setup_parser(parser, args, self.goals)
Ejemplo n.º 21
0
class CmdLineSpecParserTest(BaseTest):

  @property
  def alias_groups(self):
    return BuildFileAliases(
      targets={
        'generic': Target
      }
    )

  def setUp(self):
    super(CmdLineSpecParserTest, self).setUp()

    def add_target(path, name):
      self.add_to_build_file(path, 'generic(name="{name}")\n'.format(name=name))

    add_target('BUILD', 'root')
    add_target('a', 'a')
    add_target('a', 'b')
    add_target('a/b', 'b')
    add_target('a/b', 'c')

    self.spec_parser = CmdLineSpecParser(self.build_root, self.address_mapper)

  def test_normal(self):
    self.assert_parsed(cmdline_spec=':root', expected=[':root'])
    self.assert_parsed(cmdline_spec='//:root', expected=[':root'])

    self.assert_parsed(cmdline_spec='a', expected=['a'])
    self.assert_parsed(cmdline_spec='a:a', expected=['a'])

    self.assert_parsed(cmdline_spec='a/b', expected=['a/b'])
    self.assert_parsed(cmdline_spec='a/b:b', expected=['a/b'])
    self.assert_parsed(cmdline_spec='a/b:c', expected=['a/b:c'])

  def test_sibling(self):
    self.assert_parsed(cmdline_spec=':', expected=[':root'])
    self.assert_parsed(cmdline_spec='//:', expected=[':root'])

    self.assert_parsed(cmdline_spec='a:', expected=['a', 'a:b'])
    self.assert_parsed(cmdline_spec='//a:', expected=['a', 'a:b'])

    self.assert_parsed(cmdline_spec='a/b:', expected=['a/b', 'a/b:c'])
    self.assert_parsed(cmdline_spec='//a/b:', expected=['a/b', 'a/b:c'])

  def test_sibling_or_descendents(self):
    self.assert_parsed(cmdline_spec='::', expected=[':root', 'a', 'a:b', 'a/b', 'a/b:c'])
    self.assert_parsed(cmdline_spec='//::', expected=[':root', 'a', 'a:b', 'a/b', 'a/b:c'])

    self.assert_parsed(cmdline_spec='a::', expected=['a', 'a:b', 'a/b', 'a/b:c'])
    self.assert_parsed(cmdline_spec='//a::', expected=['a', 'a:b', 'a/b', 'a/b:c'])

    self.assert_parsed(cmdline_spec='a/b::', expected=['a/b', 'a/b:c'])
    self.assert_parsed(cmdline_spec='//a/b::', expected=['a/b', 'a/b:c'])

  def test_absolute(self):
    self.assert_parsed(cmdline_spec=os.path.join(self.build_root, 'a'), expected=['a'])
    self.assert_parsed(cmdline_spec=os.path.join(self.build_root, 'a:a'), expected=['a'])
    self.assert_parsed(cmdline_spec=os.path.join(self.build_root, 'a:'), expected=['a', 'a:b'])
    self.assert_parsed(cmdline_spec=os.path.join(self.build_root, 'a::'),
                       expected=['a', 'a:b', 'a/b', 'a/b:c'])

    double_absolute = '/' + os.path.join(self.build_root, 'a')
    self.assertEquals('//', double_absolute[:2],
                      'A sanity check we have a leading-// absolute spec')
    with self.assertRaises(self.spec_parser.BadSpecError):
      self.spec_parser.parse_addresses(double_absolute).next()

    with self.assertRaises(self.spec_parser.BadSpecError):
      self.spec_parser.parse_addresses('/not/the/buildroot/a').next()

  def test_cmd_line_affordances(self):
    self.assert_parsed(cmdline_spec='./:root', expected=[':root'])
    self.assert_parsed(cmdline_spec='//./:root', expected=[':root'])
    self.assert_parsed(cmdline_spec='//./a/../:root', expected=[':root'])
    self.assert_parsed(cmdline_spec=os.path.join(self.build_root, './a/../:root'),
                       expected=[':root'])

    self.assert_parsed(cmdline_spec='a/', expected=['a'])
    self.assert_parsed(cmdline_spec='./a/', expected=['a'])
    self.assert_parsed(cmdline_spec=os.path.join(self.build_root, './a/'), expected=['a'])

    self.assert_parsed(cmdline_spec='a/b/:b', expected=['a/b'])
    self.assert_parsed(cmdline_spec='./a/b/:b', expected=['a/b'])
    self.assert_parsed(cmdline_spec=os.path.join(self.build_root, './a/b/:b'), expected=['a/b'])

  def test_cmd_line_spec_list(self):
    self.assert_parsed_list(cmdline_spec_list=['a', 'a/b'], expected=['a', 'a/b'])
    self.assert_parsed_list(cmdline_spec_list=['::'], expected=[':root', 'a', 'a:b', 'a/b', 'a/b:c'])

  def test_does_not_exist(self):
    with self.assertRaises(self.spec_parser.BadSpecError):
      self.spec_parser.parse_addresses('c').next()

    with self.assertRaises(self.spec_parser.BadSpecError):
      self.spec_parser.parse_addresses('c:').next()

    with self.assertRaises(self.spec_parser.BadSpecError):
      self.spec_parser.parse_addresses('c::').next()

  def assert_parsed(self, cmdline_spec, expected):
    def sort(addresses):
      return sorted(addresses, key=lambda address: address.spec)

    self.assertEqual(sort(Address.parse(addr) for addr in expected),
                     sort(self.spec_parser.parse_addresses(cmdline_spec)))

  def assert_parsed_list(self, cmdline_spec_list, expected):
    def sort(addresses):
      return sorted(addresses, key=lambda address: address.spec)

    self.assertEqual(sort(Address.parse(addr) for addr in expected),
                     sort(self.spec_parser.parse_addresses(cmdline_spec_list)))

  def test_spec_excludes(self):
    expected_specs = [':root', 'a', 'a:b', 'a/b', 'a/b:c']

    # This bogus BUILD file gets in the way of parsing.
    self.add_to_build_file('some/dir', 'COMPLETELY BOGUS BUILDFILE)\n')
    with self.assertRaises(CmdLineSpecParser.BadSpecError):
      self.assert_parsed_list(cmdline_spec_list=['::'], expected=expected_specs)

    # Test absolute path in spec_excludes.
    self.spec_parser = CmdLineSpecParser(self.build_root, self.address_mapper,
                                         spec_excludes=[os.path.join(self.build_root, 'some')])
    self.assert_parsed_list(cmdline_spec_list=['::'], expected=expected_specs)

    # Test relative path in spec_excludes.
    self.spec_parser = CmdLineSpecParser(self.build_root, self.address_mapper,
                                         spec_excludes=['some'])
    self.assert_parsed_list(cmdline_spec_list=['::'], expected=expected_specs)

  def test_exclude_target_regexps(self):
    expected_specs = [':root', 'a', 'a:b', 'a/b', 'a/b:c']

    # This bogus BUILD file gets in the way of parsing.
    self.add_to_build_file('some/dir', 'COMPLETELY BOGUS BUILDFILE)\n')
    with self.assertRaises(CmdLineSpecParser.BadSpecError):
      self.assert_parsed_list(cmdline_spec_list=['::'], expected=expected_specs)

    self.spec_parser = CmdLineSpecParser(self.build_root, self.address_mapper,
                                         exclude_target_regexps=[r'.*some/dir.*'])
    self.assert_parsed_list(cmdline_spec_list=['::'], expected=expected_specs)
Ejemplo n.º 22
0
    def setup_parser(self, parser, args):
        if not args:
            args.append('help')

        logger = logging.getLogger(__name__)

        goals = self.new_options.goals
        specs = self.new_options.target_specs
        fail_fast = self.new_options.for_global_scope().fail_fast

        for goal in goals:
            if BuildFile.from_cache(get_buildroot(), goal,
                                    must_exist=False).exists():
                logger.warning(
                    " Command-line argument '{0}' is ambiguous and was assumed to be "
                    "a goal. If this is incorrect, disambiguate it with ./{0}."
                    .format(goal))

        if self.new_options.is_help:
            self.new_options.print_help(goals=goals)
            sys.exit(0)

        self.requested_goals = goals

        with self.run_tracker.new_workunit(name='setup',
                                           labels=[WorkUnit.SETUP]):
            spec_parser = CmdLineSpecParser(
                self.root_dir,
                self.address_mapper,
                spec_excludes=self.get_spec_excludes())
            with self.run_tracker.new_workunit(name='parse',
                                               labels=[WorkUnit.SETUP]):
                for spec in specs:
                    for address in spec_parser.parse_addresses(
                            spec, fail_fast):
                        self.build_graph.inject_address_closure(address)
                        self.targets.append(
                            self.build_graph.get_target(address))
        self.goals = [Goal.by_name(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 goal in Engine.execution_order(self.goals):
                for task_name in goal.ordered_task_names():
                    sections.add(task_name)
                    task_type = goal.task_type_by_name(task_name)
                    for clazz in 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:
                # TODO(John Sirois): Cleanup this currently important mutation of the passed in args
                # once the 2-layer of command -> goal is squashed into one.
                args[:] = augmented_args
                sys.stderr.write("(using pantsrc expansion: pants goal %s)\n" %
                                 ' '.join(augmented_args))
Ejemplo n.º 23
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
        non_help_args = filter(lambda f: f not in help_flags, args)

        goals, specs = GoalRunner.parse_args(non_help_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 = CmdLineSpecParser(self.root_dir, self.address_mapper)
            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_graph.inject_address_closure(address)
                        self.targets.append(self.build_graph.get_target(address))
        self.goals = [Goal.by_name(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 goal in Engine.execution_order(self.goals):
                for task_name in goal.ordered_task_names():
                    sections.add(task_name)
                    task_type = goal.task_type_by_name(task_name)
                    for clazz in 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:
                # TODO(John Sirois): Cleanup this currently important mutation of the passed in args
                # once the 2-layer of command -> goal is squashed into one.
                del args[:]
                args.extend(augmented_args)
                sys.stderr.write("(using pantsrc expansion: pants goal %s)\n" % " ".join(augmented_args))

        Goal.setup_parser(parser, args, self.goals)
Ejemplo n.º 24
0
class GoalRunnerFactory(object):
  def __init__(self, root_dir, options, build_config, run_tracker, reporting, exiter=sys.exit):
    """
    :param str root_dir: The root directory of the pants workspace (aka the "build root").
    :param Options options: The global, pre-initialized Options instance.
    :param BuildConfiguration build_config: A pre-initialized BuildConfiguration instance.
    :param Runtracker run_tracker: The global, pre-initialized/running RunTracker instance.
    :param Reporting reporting: The global, pre-initialized Reporting instance.
    :param func exiter: A function that accepts an exit code value and exits (for tests, Optional).
    """
    self._root_dir = root_dir
    self._options = options
    self._build_config = build_config
    self._run_tracker = run_tracker
    self._reporting = reporting
    self._exiter = exiter

    self._goals = []
    self._targets = []
    self._requested_goals = self._options.goals
    self._target_specs = self._options.target_specs
    self._help_request = self._options.help_request

    self._global_options = options.for_global_scope()
    self._tag = self._global_options.tag
    self._fail_fast = self._global_options.fail_fast
    self._spec_excludes = self._global_options.spec_excludes
    self._explain = self._global_options.explain
    self._kill_nailguns = self._global_options.kill_nailguns

    self._build_file_type = self._get_buildfile_type(self._global_options.build_file_rev)
    self._build_file_parser = BuildFileParser(self._build_config, self._root_dir)
    self._address_mapper = BuildFileAddressMapper(self._build_file_parser, self._build_file_type)
    self._build_graph = BuildGraph(self._address_mapper)
    self._spec_parser = CmdLineSpecParser(
      self._root_dir,
      self._address_mapper,
      spec_excludes=self._spec_excludes,
      exclude_target_regexps=self._global_options.exclude_target_regexp
    )

  def _get_buildfile_type(self, build_file_rev):
    """Selects the BuildFile type for use in a given pants run."""
    if build_file_rev:
      ScmBuildFile.set_rev(build_file_rev)
      ScmBuildFile.set_scm(get_scm())
      return ScmBuildFile
    else:
      return FilesystemBuildFile

  def _expand_goals(self, goals):
    """Check and populate the requested goals for a given run."""
    for goal in goals:
      if self._address_mapper.from_cache(self._root_dir, goal, must_exist=False).file_exists():
        logger.warning("Command-line argument '{0}' is ambiguous and was assumed to be "
                       "a goal. If this is incorrect, disambiguate it with ./{0}.".format(goal))

    if self._help_request:
      help_printer = HelpPrinter(self._options)
      help_printer.print_help()
      self._exiter(0)

    self._goals.extend([Goal.by_name(goal) for goal in goals])

  def _expand_specs(self, specs, fail_fast):
    """Populate the BuildGraph and target list from a set of input specs."""
    with self._run_tracker.new_workunit(name='parse', labels=[WorkUnitLabel.SETUP]):
      def filter_for_tag(tag):
        return lambda target: tag in map(str, target.tags)

      tag_filter = wrap_filters(create_filters(self._tag, filter_for_tag))

      for spec in specs:
        for address in self._spec_parser.parse_addresses(spec, fail_fast):
          self._build_graph.inject_address_closure(address)
          target = self._build_graph.get_target(address)
          if tag_filter(target):
            self._targets.append(target)

  def _is_quiet(self):
    return any(goal.has_task_of_type(QuietTaskMixin) for goal in self._goals) or self._explain

  def _setup_context(self):
    # TODO(John Sirois): Kill when source root registration is lifted out of BUILD files.
    with self._run_tracker.new_workunit(name='bootstrap', labels=[WorkUnitLabel.SETUP]):
      source_root_bootstrapper = SourceRootBootstrapper.global_instance()
      source_root_bootstrapper.bootstrap(self._address_mapper, self._build_file_parser)

    with self._run_tracker.new_workunit(name='setup', labels=[WorkUnitLabel.SETUP]):
      self._expand_goals(self._requested_goals)
      self._expand_specs(self._target_specs, self._fail_fast)

      # Now that we've parsed the bootstrap BUILD files, and know about the SCM system.
      self._run_tracker.run_info.add_scm_info()

      # Update the Reporting settings now that we have options and goal info.
      invalidation_report = self._reporting.update_reporting(self._global_options,
                                                             self._is_quiet(),
                                                             self._run_tracker)

      context = Context(options=self._options,
                        run_tracker=self._run_tracker,
                        target_roots=self._targets,
                        requested_goals=self._requested_goals,
                        build_graph=self._build_graph,
                        build_file_parser=self._build_file_parser,
                        address_mapper=self._address_mapper,
                        spec_excludes=self._spec_excludes,
                        invalidation_report=invalidation_report)

    return context, invalidation_report

  def setup(self):
    context, invalidation_report = self._setup_context()
    return GoalRunner(context=context,
                      goals=self._goals,
                      kill_nailguns=self._kill_nailguns,
                      run_tracker=self._run_tracker,
                      invalidation_report=invalidation_report)
Ejemplo n.º 25
0
class CmdLineSpecParserTest(BaseTest):
    @property
    def alias_groups(self):
        return BuildFileAliases.create(targets={'generic': Target})

    def setUp(self):
        super(CmdLineSpecParserTest, self).setUp()

        def add_target(path, name):
            self.add_to_build_file(
                path, 'generic(name="{name}")\n'.format(name=name))

        add_target('BUILD', 'root')
        add_target('a', 'a')
        add_target('a', 'b')
        add_target('a/b', 'b')
        add_target('a/b', 'c')

        self.spec_parser = CmdLineSpecParser(self.build_root,
                                             self.address_mapper)

    def test_normal(self):
        self.assert_parsed(cmdline_spec=':root', expected=[':root'])
        self.assert_parsed(cmdline_spec='//:root', expected=[':root'])

        self.assert_parsed(cmdline_spec='a', expected=['a'])
        self.assert_parsed(cmdline_spec='a:a', expected=['a'])

        self.assert_parsed(cmdline_spec='a/b', expected=['a/b'])
        self.assert_parsed(cmdline_spec='a/b:b', expected=['a/b'])
        self.assert_parsed(cmdline_spec='a/b:c', expected=['a/b:c'])

    def test_sibling(self):
        self.assert_parsed(cmdline_spec=':', expected=[':root'])
        self.assert_parsed(cmdline_spec='//:', expected=[':root'])

        self.assert_parsed(cmdline_spec='a:', expected=['a', 'a:b'])
        self.assert_parsed(cmdline_spec='//a:', expected=['a', 'a:b'])

        self.assert_parsed(cmdline_spec='a/b:', expected=['a/b', 'a/b:c'])
        self.assert_parsed(cmdline_spec='//a/b:', expected=['a/b', 'a/b:c'])

    def test_sibling_or_descendents(self):
        self.assert_parsed(cmdline_spec='::',
                           expected=[':root', 'a', 'a:b', 'a/b', 'a/b:c'])
        self.assert_parsed(cmdline_spec='//::',
                           expected=[':root', 'a', 'a:b', 'a/b', 'a/b:c'])

        self.assert_parsed(cmdline_spec='a::',
                           expected=['a', 'a:b', 'a/b', 'a/b:c'])
        self.assert_parsed(cmdline_spec='//a::',
                           expected=['a', 'a:b', 'a/b', 'a/b:c'])

        self.assert_parsed(cmdline_spec='a/b::', expected=['a/b', 'a/b:c'])
        self.assert_parsed(cmdline_spec='//a/b::', expected=['a/b', 'a/b:c'])

    def test_absolute(self):
        self.assert_parsed(cmdline_spec=os.path.join(self.build_root, 'a'),
                           expected=['a'])
        self.assert_parsed(cmdline_spec=os.path.join(self.build_root, 'a:a'),
                           expected=['a'])
        self.assert_parsed(cmdline_spec=os.path.join(self.build_root, 'a:'),
                           expected=['a', 'a:b'])
        self.assert_parsed(cmdline_spec=os.path.join(self.build_root, 'a::'),
                           expected=['a', 'a:b', 'a/b', 'a/b:c'])

        double_absolute = '/' + os.path.join(self.build_root, 'a')
        self.assertEquals('//', double_absolute[:2],
                          'A sanity check we have a leading-// absolute spec')
        with self.assertRaises(self.spec_parser.BadSpecError):
            self.spec_parser.parse_addresses(double_absolute).next()

        with self.assertRaises(self.spec_parser.BadSpecError):
            self.spec_parser.parse_addresses('/not/the/buildroot/a').next()

    def test_cmd_line_affordances(self):
        self.assert_parsed(cmdline_spec='./:root', expected=[':root'])
        self.assert_parsed(cmdline_spec='//./:root', expected=[':root'])
        self.assert_parsed(cmdline_spec='//./a/../:root', expected=[':root'])
        self.assert_parsed(cmdline_spec=os.path.join(self.build_root,
                                                     './a/../:root'),
                           expected=[':root'])

        self.assert_parsed(cmdline_spec='a/', expected=['a'])
        self.assert_parsed(cmdline_spec='./a/', expected=['a'])
        self.assert_parsed(cmdline_spec=os.path.join(self.build_root, './a/'),
                           expected=['a'])

        self.assert_parsed(cmdline_spec='a/b/:b', expected=['a/b'])
        self.assert_parsed(cmdline_spec='./a/b/:b', expected=['a/b'])
        self.assert_parsed(cmdline_spec=os.path.join(self.build_root,
                                                     './a/b/:b'),
                           expected=['a/b'])

    def test_cmd_line_spec_list(self):
        self.assert_parsed_list(cmdline_spec_list=['a', 'a/b'],
                                expected=['a', 'a/b'])
        self.assert_parsed_list(cmdline_spec_list=['a', 'a/b', '^a/b'],
                                expected=['a'])
        self.assert_parsed_list(cmdline_spec_list=['^a/b', 'a', 'a/b'],
                                expected=['a'])
        self.assert_parsed_list(cmdline_spec_list=['::'],
                                expected=[':root', 'a', 'a:b', 'a/b', 'a/b:c'])
        self.assert_parsed_list(cmdline_spec_list=['::', '^a/b::'],
                                expected=[':root', 'a', 'a:b'])
        self.assert_parsed_list(cmdline_spec_list=['^a/b::', '::'],
                                expected=[':root', 'a', 'a:b'])

    def test_does_not_exist(self):
        with self.assertRaises(self.spec_parser.BadSpecError):
            self.spec_parser.parse_addresses('c').next()

        with self.assertRaises(self.spec_parser.BadSpecError):
            self.spec_parser.parse_addresses('c:').next()

        with self.assertRaises(self.spec_parser.BadSpecError):
            self.spec_parser.parse_addresses('c::').next()

    def assert_parsed(self, cmdline_spec, expected):
        def sort(addresses):
            return sorted(addresses, key=lambda address: address.spec)

        self.assertEqual(
            sort(SyntheticAddress.parse(addr) for addr in expected),
            sort(self.spec_parser.parse_addresses(cmdline_spec)))

    def assert_parsed_list(self, cmdline_spec_list, expected):
        def sort(addresses):
            return sorted(addresses, key=lambda address: address.spec)

        self.assertEqual(
            sort(SyntheticAddress.parse(addr) for addr in expected),
            sort(self.spec_parser.parse_addresses(cmdline_spec_list)))

    def test_pants_dot_d_excluded(self):
        expected_specs = [':root', 'a', 'a:b', 'a/b', 'a/b:c']

        # This bogus BUILD file gets in the way of parsing.
        self.add_to_build_file('.pants.d/some/dir',
                               'COMPLETELY BOGUS BUILDFILE)\n')
        with self.assertRaises(CmdLineSpecParser.BadSpecError):
            self.assert_parsed_list(cmdline_spec_list=['::'],
                                    expected=expected_specs)

        self.spec_parser = CmdLineSpecParser(
            self.build_root,
            self.address_mapper,
            spec_excludes=[os.path.join(self.build_root, '.pants.d')])
        self.assert_parsed_list(cmdline_spec_list=['::'],
                                expected=expected_specs)
Ejemplo n.º 26
0
class CmdLineSpecParserTest(BaseTest):
  @property
  def alias_groups(self):
    return BuildFileAliases.create(
      targets={
        'generic': Target
      }
    )

  def setUp(self):
    super(CmdLineSpecParserTest, self).setUp()

    def add_target(path, name):
      self.add_to_build_file(path, 'generic(name="{name}")\n'.format(name=name))

    add_target('BUILD', 'root')
    add_target('a', 'a')
    add_target('a', 'b')
    add_target('a/b', 'b')
    add_target('a/b', 'c')

    self.spec_parser = CmdLineSpecParser(self.build_root, self.address_mapper)

  def test_normal(self):
    self.assert_parsed(cmdline_spec=':root', expected=[':root'])
    self.assert_parsed(cmdline_spec='//:root', expected=[':root'])

    self.assert_parsed(cmdline_spec='a', expected=['a'])
    self.assert_parsed(cmdline_spec='a:a', expected=['a'])

    self.assert_parsed(cmdline_spec='a/b', expected=['a/b'])
    self.assert_parsed(cmdline_spec='a/b:b', expected=['a/b'])
    self.assert_parsed(cmdline_spec='a/b:c', expected=['a/b:c'])

  def test_sibling(self):
    self.assert_parsed(cmdline_spec=':', expected=[':root'])
    self.assert_parsed(cmdline_spec='//:', expected=[':root'])

    self.assert_parsed(cmdline_spec='a:', expected=['a', 'a:b'])
    self.assert_parsed(cmdline_spec='//a:', expected=['a', 'a:b'])

    self.assert_parsed(cmdline_spec='a/b:', expected=['a/b', 'a/b:c'])
    self.assert_parsed(cmdline_spec='//a/b:', expected=['a/b', 'a/b:c'])

  def test_sibling_or_descendents(self):
    self.assert_parsed(cmdline_spec='::', expected=[':root', 'a', 'a:b', 'a/b', 'a/b:c'])
    self.assert_parsed(cmdline_spec='//::', expected=[':root', 'a', 'a:b', 'a/b', 'a/b:c'])

    self.assert_parsed(cmdline_spec='a::', expected=['a', 'a:b', 'a/b', 'a/b:c'])
    self.assert_parsed(cmdline_spec='//a::', expected=['a', 'a:b', 'a/b', 'a/b:c'])

    self.assert_parsed(cmdline_spec='a/b::', expected=['a/b', 'a/b:c'])
    self.assert_parsed(cmdline_spec='//a/b::', expected=['a/b', 'a/b:c'])

  def test_absolute(self):
    self.assert_parsed(cmdline_spec=os.path.join(self.build_root, 'a'), expected=['a'])
    self.assert_parsed(cmdline_spec=os.path.join(self.build_root, 'a:a'), expected=['a'])
    self.assert_parsed(cmdline_spec=os.path.join(self.build_root, 'a:'), expected=['a', 'a:b'])
    self.assert_parsed(cmdline_spec=os.path.join(self.build_root, 'a::'),
                       expected=['a', 'a:b', 'a/b', 'a/b:c'])

    double_absolute = '/' + os.path.join(self.build_root, 'a')
    self.assertEquals('//', double_absolute[:2],
                      'A sanity check we have a leading-// absolute spec')
    with self.assertRaises(self.spec_parser.BadSpecError):
      self.spec_parser.parse_addresses(double_absolute).next()

    with self.assertRaises(self.spec_parser.BadSpecError):
      self.spec_parser.parse_addresses('/not/the/buildroot/a').next()

  def test_cmd_line_affordances(self):
    self.assert_parsed(cmdline_spec='./:root', expected=[':root'])
    self.assert_parsed(cmdline_spec='//./:root', expected=[':root'])
    self.assert_parsed(cmdline_spec='//./a/../:root', expected=[':root'])
    self.assert_parsed(cmdline_spec=os.path.join(self.build_root, './a/../:root'),
                       expected=[':root'])

    self.assert_parsed(cmdline_spec='a/', expected=['a'])
    self.assert_parsed(cmdline_spec='./a/', expected=['a'])
    self.assert_parsed(cmdline_spec=os.path.join(self.build_root, './a/'), expected=['a'])

    self.assert_parsed(cmdline_spec='a/b/:b', expected=['a/b'])
    self.assert_parsed(cmdline_spec='./a/b/:b', expected=['a/b'])
    self.assert_parsed(cmdline_spec=os.path.join(self.build_root, './a/b/:b'), expected=['a/b'])

  def test_cmd_line_spec_list(self):
    self.assert_parsed_list(cmdline_spec_list=['a', 'a/b'], expected=['a', 'a/b'])
    self.assert_parsed_list(cmdline_spec_list=['a', 'a/b', '^a/b'], expected=['a'])
    self.assert_parsed_list(cmdline_spec_list=['^a/b', 'a', 'a/b'], expected=['a'])
    self.assert_parsed_list(cmdline_spec_list=['::'], expected=[':root', 'a', 'a:b', 'a/b', 'a/b:c'])
    self.assert_parsed_list(cmdline_spec_list=['::', '^a/b::'], expected=[':root', 'a', 'a:b'])
    self.assert_parsed_list(cmdline_spec_list=['^a/b::', '::'], expected=[':root', 'a', 'a:b'])

  def test_does_not_exist(self):
    with self.assertRaises(self.spec_parser.BadSpecError):
      self.spec_parser.parse_addresses('c').next()

    with self.assertRaises(self.spec_parser.BadSpecError):
      self.spec_parser.parse_addresses('c:').next()

    with self.assertRaises(self.spec_parser.BadSpecError):
      self.spec_parser.parse_addresses('c::').next()

  def assert_parsed(self, cmdline_spec, expected):
    def sort(addresses):
      return sorted(addresses, key=lambda address: address.spec)

    self.assertEqual(sort(SyntheticAddress.parse(addr) for addr in expected),
                     sort(self.spec_parser.parse_addresses(cmdline_spec)))

  def assert_parsed_list(self, cmdline_spec_list, expected):
    def sort(addresses):
      return sorted(addresses, key=lambda address: address.spec)

    self.assertEqual(sort(SyntheticAddress.parse(addr) for addr in expected),
                     sort(self.spec_parser.parse_addresses(cmdline_spec_list)))