Exemple #1
0
  def setUp(self):
    super(BaseTest, self).setUp()
    Goal.clear()
    Subsystem.reset()

    self.real_build_root = BuildRoot().path

    self.build_root = os.path.realpath(mkdtemp(suffix='_BUILD_ROOT'))
    self.addCleanup(safe_rmtree, self.build_root)

    self.pants_workdir = os.path.join(self.build_root, '.pants.d')
    safe_mkdir(self.pants_workdir)

    self.options = defaultdict(dict)  # scope -> key-value mapping.
    self.options[''] = {
      'pants_workdir': self.pants_workdir,
      'pants_supportdir': os.path.join(self.build_root, 'build-support'),
      'pants_distdir': os.path.join(self.build_root, 'dist'),
      'pants_configdir': os.path.join(self.build_root, 'config'),
      'cache_key_gen_version': '0-test',
    }

    BuildRoot().path = self.build_root
    self.addCleanup(BuildRoot().reset)

    # We need a pants.ini, even if empty. get_buildroot() uses its presence.
    self.create_file('pants.ini')
    self._build_configuration = BuildConfiguration()
    self._build_configuration.register_aliases(self.alias_groups)
    self.build_file_parser = BuildFileParser(self._build_configuration, self.build_root)
    self.address_mapper = BuildFileAddressMapper(self.build_file_parser, FilesystemBuildFile)
    self.build_graph = BuildGraph(address_mapper=self.address_mapper)
Exemple #2
0
    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
        # Will be provided through context.address_mapper.build_ignore_patterns.
        self._spec_excludes = None
        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)
        build_ignore_patterns = self._global_options.ignore_patterns or []
        build_ignore_patterns.extend(
            BuildFile._spec_excludes_to_gitignore_syntax(
                self._root_dir, self._global_options.spec_excludes))
        self._address_mapper = BuildFileAddressMapper(self._build_file_parser,
                                                      self._project_tree,
                                                      build_ignore_patterns)
        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)
Exemple #3
0
  def scan(self, root=None):
    """Scans and parses all BUILD files found under ``root``.

    Only BUILD files found under ``root`` are parsed as roots in the graph, but any dependencies of
    targets parsed in the root tree's BUILD files will be followed and this may lead to BUILD files
    outside of ``root`` being parsed and included in the returned build graph.

    :param string root: The path to scan; by default, the build root.
    :returns: A new build graph encapsulating the targets found.
    """
    build_graph = BuildGraph(self.address_mapper)
    for address in self.address_mapper.scan_addresses(root, spec_excludes=self.spec_excludes):
      build_graph.inject_address_closure(address)
    return build_graph
Exemple #4
0
  def scan(self, root=None):
    """Scans and parses all BUILD files found under ``root``.

    Only BUILD files found under ``root`` are parsed as roots in the graph, but any dependencies of
    targets parsed in the root tree's BUILD files will be followed and this may lead to BUILD files
    outside of ``root`` being parsed and included in the returned build graph.

    :param string root: The path to scan; by default, the build root.
    :returns: A new build graph encapsulating the targets found.
    """
    build_graph = BuildGraph(self.address_mapper)
    for address in self.address_mapper.scan_addresses(root, spec_excludes=self.spec_excludes):
      build_graph.inject_address_closure(address)
    return build_graph
 def _filter_by_excludes(self, classpath_target_tuples, root_targets):
   # Excludes are always applied transitively, so regardless of whether a transitive
   # set of targets was included here, their closure must be included.
   closure = BuildGraph.closure(root_targets, bfs=True)
   excludes = self._excludes.get_for_targets(closure)
   return [target_tuple for target_tuple in classpath_target_tuples
           if _not_excluded_filter(excludes)(target_tuple)]
Exemple #6
0
 def _filter_by_excludes(self, classpath_target_tuples, root_targets):
   # Excludes are always applied transitively, so regardless of whether a transitive
   # set of targets was included here, their closure must be included.
   closure = BuildGraph.closure(root_targets, bfs=True)
   excludes = self._excludes.get_for_targets(closure)
   return [target_tuple for target_tuple in classpath_target_tuples
           if _not_excluded_filter(excludes)(target_tuple)]
Exemple #7
0
  def classpath(self, targets, classpath_prefix=None, classpath_product=None, exclude_scopes=None,
                include_scopes=None):
    """Builds a transitive classpath for the given targets.

    Optionally includes a classpath prefix or building from a non-default classpath product.

    :param targets: the targets for which to build the transitive classpath.
    :param classpath_prefix: optional additional entries to prepend to the classpath.
    :param classpath_product: an optional ClasspathProduct from which to build the classpath. if not
    specified, the runtime_classpath will be used.
    :param :class:`pants.build_graph.target_scopes.Scope` exclude_scopes: Exclude targets which
      have at least one of these scopes on the classpath.
    :param :class:`pants.build_graph.target_scopes.Scope` include_scopes: Only include targets which
      have at least one of these scopes on the classpath. Defaults to Scopes.JVM_RUNTIME_SCOPES.
    :return: a list of classpath strings.
    """
    include_scopes = Scopes.JVM_RUNTIME_SCOPES if include_scopes is None else include_scopes
    classpath_product = classpath_product or self.context.products.get_data('runtime_classpath')
    closure = BuildGraph.closure(targets, bfs=True, include_scopes=include_scopes,
                                 exclude_scopes=exclude_scopes, respect_intransitive=True)

    classpath_for_targets = ClasspathUtil.classpath(closure, classpath_product, self.confs)
    classpath = list(classpath_prefix or ())
    classpath.extend(classpath_for_targets)
    return classpath
Exemple #8
0
    def _compute_classpath(runtime_classpath, targets):
        closure = BuildGraph.closure(
            targets, bfs=True, include_scopes=Scopes.JVM_RUNTIME_SCOPES, respect_intransitive=True
        )
        classpath_for_targets = ClasspathUtil.classpath(closure, runtime_classpath)

        return classpath_for_targets
Exemple #9
0
    def classpath(self,
                  targets,
                  classpath_prefix=None,
                  classpath_product=None):
        """Builds a transitive classpath for the given targets.

    Optionally includes a classpath prefix or building from a non-default classpath product.

    :param targets: the targets for which to build the transitive classpath.
    :param classpath_prefix: optional additional entries to prepend to the classpath.
    :param classpath_product: an optional ClasspathProduct from which to build the classpath. if not
    specified, the runtime_classpath will be used.
    :return: a list of classpath strings.
    """
        classpath = list(classpath_prefix) if classpath_prefix else []

        classpath_product = classpath_product or self.context.products.get_data(
            'runtime_classpath')

        closure = BuildGraph.closure(targets, bfs=True)

        classpath_for_targets = ClasspathUtil.classpath(
            closure, classpath_product, self.confs)
        classpath.extend(classpath_for_targets)
        return classpath
    def test_closure_bfs(self):
        root = self.inject_graph(
            'a', {
                'a': ['b', 'c'],
                'b': ['d', 'e'],
                'c': ['f', 'g'],
                'd': ['h', 'i'],
                'e': ['j', 'k'],
                'f': ['l', 'm'],
                'g': ['n', 'o'],
                'h': [],
                'i': [],
                'j': [],
                'k': [],
                'l': [],
                'm': [],
                'n': [],
                'o': [],
            })

        bfs_closure = BuildGraph.closure([self.build_graph.get_target(root)],
                                         bfs=True)
        self.assertEquals(
            [t.address.target_name for t in bfs_closure],
            [
                str(six.unichr(x))
                for x in six.moves.xrange(ord('a'),
                                          ord('o') + 1)
            ],
        )
Exemple #11
0
    def test_closure_bfs(self):
        root = self.inject_graph(
            "a",
            {
                "a": ["b", "c"],
                "b": ["d", "e"],
                "c": ["f", "g"],
                "d": ["h", "i"],
                "e": ["j", "k"],
                "f": ["l", "m"],
                "g": ["n", "o"],
                "h": [],
                "i": [],
                "j": [],
                "k": [],
                "l": [],
                "m": [],
                "n": [],
                "o": [],
            },
        )

        bfs_closure = BuildGraph.closure([self.build_graph.get_target(root)],
                                         bfs=True)
        self.assertEqual(
            [t.address.target_name for t in bfs_closure],
            [chr(x) for x in range(ord("a"),
                                   ord("o") + 1)],
        )
Exemple #12
0
    def test_create_bad_targets(self):
        with self.assertRaises(TypeError):
            BuildFileAliases(targets={'fred': object()})

        target = Target('fred', Address.parse('a:b'),
                        BuildGraph(address_mapper=None))
        with self.assertRaises(TypeError):
            BuildFileAliases(targets={'fred': target})
Exemple #13
0
    def setUp(self):
        """
    :API: public
    """
        super(BaseTest, self).setUp()
        Goal.clear()
        Subsystem.reset()

        self.real_build_root = BuildRoot().path

        self.build_root = os.path.realpath(mkdtemp(suffix='_BUILD_ROOT'))
        self.addCleanup(safe_rmtree, self.build_root)

        self.pants_workdir = os.path.join(self.build_root, '.pants.d')
        safe_mkdir(self.pants_workdir)

        self.options = defaultdict(dict)  # scope -> key-value mapping.
        self.options[''] = {
            'pants_workdir': self.pants_workdir,
            'pants_supportdir': os.path.join(self.build_root, 'build-support'),
            'pants_distdir': os.path.join(self.build_root, 'dist'),
            'pants_configdir': os.path.join(self.build_root, 'config'),
            'cache_key_gen_version': '0-test',
        }
        self.options['cache'] = {
            'read_from': [],
            'write_to': [],
        }

        BuildRoot().path = self.build_root
        self.addCleanup(BuildRoot().reset)

        self._build_configuration = BuildConfiguration()
        self._build_configuration.register_aliases(self.alias_groups)
        self.build_file_parser = BuildFileParser(self._build_configuration,
                                                 self.build_root)
        self.project_tree = FileSystemProjectTree(self.build_root)
        self.address_mapper = BuildFileAddressMapper(
            self.build_file_parser,
            self.project_tree,
            build_ignore_patterns=self.build_ignore_patterns)
        self.build_graph = BuildGraph(address_mapper=self.address_mapper)
Exemple #14
0
  def test_closure_bfs(self):
    root = self.inject_graph('a', {
      'a': ['b', 'c'],
      'b': ['d', 'e'],
      'c': ['f', 'g'],
      'd': ['h', 'i'],
      'e': ['j', 'k'],
      'f': ['l', 'm'],
      'g': ['n', 'o'],
      'h': [], 'i': [], 'j': [], 'k': [], 'l': [], 'm': [], 'n': [], 'o': [],
    })

    bfs_closure = BuildGraph.closure([self.build_graph.get_target(root)], bfs=True)
    self.assertEquals(
        [t.address.target_name for t in bfs_closure],
        [str(six.unichr(x)) for x in six.moves.xrange(ord('a'), ord('o') + 1)],
    )
Exemple #15
0
  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
    # Will be provided through context.address_mapper.build_ignore_patterns.
    self._spec_excludes = None
    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)
    build_ignore_patterns = self._global_options.ignore_patterns or []
    build_ignore_patterns.extend(BuildFile._spec_excludes_to_gitignore_syntax(self._root_dir,
                                                                              self._global_options.spec_excludes))
    self._address_mapper = BuildFileAddressMapper(self._build_file_parser, self._project_tree, build_ignore_patterns)
    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
    )
Exemple #16
0
    def test_closure(self):
        self.assertEqual(OrderedSet(), BuildGraph.closure([]))
        a = self.make_target("a")
        self.assertEqual(OrderedSet([a]), BuildGraph.closure([a]))
        b = self.make_target("b", dependencies=[a])
        self.assertEqual(OrderedSet([b, a]), BuildGraph.closure([b]))
        c = self.make_target("c", dependencies=[b])
        self.assertEqual(OrderedSet([c, b, a]), BuildGraph.closure([c]))
        d = self.make_target("d", dependencies=[a, c])
        self.assertEqual(OrderedSet([d, a, c, b]), BuildGraph.closure([d]))

        def d_gen():
            yield d

        self.assertEqual(OrderedSet([d, a, c, b]), BuildGraph.closure(d_gen()))

        empty_gen = iter(())
        self.assertEqual(OrderedSet([]), BuildGraph.closure(empty_gen))
Exemple #17
0
  def classpath(self, targets, classpath_prefix=None, classpath_product=None):
    """Builds a transitive classpath for the given targets.

    Optionally includes a classpath prefix or building from a non-default classpath product.

    :param targets: the targets for which to build the transitive classpath.
    :param classpath_prefix: optional additional entries to prepend to the classpath.
    :param classpath_product: an optional ClasspathProduct from which to build the classpath. if not
    specified, the runtime_classpath will be used.
    :return: a list of classpath strings.
    """
    classpath = list(classpath_prefix) if classpath_prefix else []

    classpath_product = classpath_product or self.context.products.get_data('runtime_classpath')

    closure = BuildGraph.closure(targets, bfs=True)

    classpath_for_targets = ClasspathUtil.classpath(closure, classpath_product, self.confs)
    classpath.extend(classpath_for_targets)
    return classpath
Exemple #18
0
  def test_closure(self):
    self.assertEquals([], BuildGraph.closure([]))
    a = self.make_target('a')
    self.assertEquals([a], BuildGraph.closure([a]))
    b = self.make_target('b', dependencies=[a])
    self.assertEquals([b, a], BuildGraph.closure([b]))
    c = self.make_target('c', dependencies=[b])
    self.assertEquals([c, b, a], BuildGraph.closure([c]))
    d = self.make_target('d', dependencies=[a, c])
    self.assertEquals([d, a, c, b], BuildGraph.closure([d]))

    def d_gen():
      yield d
    self.assertEquals([d, a, c, b], BuildGraph.closure(d_gen()))

    def empty_gen():
      return
      yield
    self.assertEquals([], BuildGraph.closure(empty_gen()))
Exemple #19
0
  def test_closure(self):
    self.assertEquals([], BuildGraph.closure([]))
    a = self.make_target('a')
    self.assertEquals([a], BuildGraph.closure([a]))
    b = self.make_target('b', dependencies=[a])
    self.assertEquals([b, a], BuildGraph.closure([b]))
    c = self.make_target('c', dependencies=[b])
    self.assertEquals([c, b, a], BuildGraph.closure([c]))
    d = self.make_target('d', dependencies=[a, c])
    self.assertEquals([d, a, c, b], BuildGraph.closure([d]))

    def d_gen():
      yield d
    self.assertEquals([d, a, c, b], BuildGraph.closure(d_gen()))

    def empty_gen():
      return
      yield
    self.assertEquals([], BuildGraph.closure(empty_gen()))
Exemple #20
0
  def setUp(self):
    """
    :API: public
    """
    super(BaseTest, self).setUp()
    Goal.clear()
    Subsystem.reset()

    self.real_build_root = BuildRoot().path

    self.build_root = os.path.realpath(mkdtemp(suffix='_BUILD_ROOT'))
    self.addCleanup(safe_rmtree, self.build_root)

    self.pants_workdir = os.path.join(self.build_root, '.pants.d')
    safe_mkdir(self.pants_workdir)

    self.options = defaultdict(dict)  # scope -> key-value mapping.
    self.options[''] = {
      'pants_workdir': self.pants_workdir,
      'pants_supportdir': os.path.join(self.build_root, 'build-support'),
      'pants_distdir': os.path.join(self.build_root, 'dist'),
      'pants_configdir': os.path.join(self.build_root, 'config'),
      'cache_key_gen_version': '0-test',
    }
    self.options['cache'] = {
      'read_from': [],
      'write_to': [],
    }

    BuildRoot().path = self.build_root
    self.addCleanup(BuildRoot().reset)

    self._build_configuration = BuildConfiguration()
    self._build_configuration.register_aliases(self.alias_groups)
    self.build_file_parser = BuildFileParser(self._build_configuration, self.build_root)
    self.project_tree = FileSystemProjectTree(self.build_root)
    self.address_mapper = BuildFileAddressMapper(self.build_file_parser, self.project_tree,
                                                 build_ignore_patterns=self.build_ignore_patterns)
    self.build_graph = BuildGraph(address_mapper=self.address_mapper)
Exemple #21
0
    def test_closure(self):
        self.assertEqual([], BuildGraph.closure([]))
        a = self.make_target('a')
        self.assertEqual([a], BuildGraph.closure([a]))
        b = self.make_target('b', dependencies=[a])
        self.assertEqual([b, a], BuildGraph.closure([b]))
        c = self.make_target('c', dependencies=[b])
        self.assertEqual([c, b, a], BuildGraph.closure([c]))
        d = self.make_target('d', dependencies=[a, c])
        self.assertEqual([d, a, c, b], BuildGraph.closure([d]))

        def d_gen():
            yield d

        self.assertEqual([d, a, c, b], BuildGraph.closure(d_gen()))

        def empty_gen():
            return
            yield  # type: ignore[misc] # MyPy complains that this is not reachable

        self.assertEqual([], BuildGraph.closure(empty_gen()))
Exemple #22
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,
        )
Exemple #23
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)
 def test_lookup_exception(self):
     # There is code that depends on the fact that TransitiveLookupError is a subclass of
     # AddressLookupError
     self.assertIsInstance(BuildGraph.TransitiveLookupError(),
                           AddressLookupError)
Exemple #25
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)
Exemple #26
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
    # Will be provided through context.address_mapper.build_ignore_patterns.
    self._spec_excludes = None
    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)
    build_ignore_patterns = self._global_options.ignore_patterns or []
    build_ignore_patterns.extend(BuildFile._spec_excludes_to_gitignore_syntax(self._root_dir,
                                                                              self._global_options.spec_excludes))
    self._address_mapper = BuildFileAddressMapper(
      self._build_file_parser,
      self._project_tree,
      build_ignore_patterns,
      exclude_target_regexps=self._global_options.exclude_target_regexp
    )
    self._build_graph = BuildGraph(self._address_mapper)

  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, spec_strs, 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))

      # Parse all specs into unique Spec objects.
      spec_parser = CmdLineSpecParser(self._root_dir)
      specs = OrderedSet()
      for spec_str in spec_strs:
        specs.add(spec_parser.parse_spec(spec_str))

      # Then scan them to generate unique Addresses.
      for address in self._address_mapper.scan_specs(specs, fail_fast, self._spec_excludes):
        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)
Exemple #27
0
  def instrument(self, output_dir):
    for datafile in self._iter_datafiles(output_dir):
      os.unlink(datafile)

    self._canonical_datafile = os.path.join(output_dir, '{}.canonical'.format(self._DATAFILE_NAME))
    # It's conceivable we'll be executing a test that has no source file dependencies; ie: we'll
    # never generate a canonical coverage datafile below. Create an empty one here to allow the
    # test run to proceeed normally.
    touch(self._canonical_datafile)

    # Setup an instrumentation classpath based on the existing runtime classpath.
    runtime_classpath = self._context.products.get_data('runtime_classpath')
    instrumentation_classpath = self._context.products.safe_create_data('instrument_classpath',
                                                                        runtime_classpath.copy)
    self.initialize_instrument_classpath(output_dir,
                                         self._settings,
                                         self._targets,
                                         instrumentation_classpath)

    cobertura_cp = self._settings.tool_classpath('cobertura-instrument')
    files_to_instrument = []
    for target in self._targets:
      if Cobertura.is_coverage_target(target):
        paths = instrumentation_classpath.get_for_target(target)
        for (name, path) in paths:
          files_to_instrument.append(path)

    if len(files_to_instrument) > 0:
      unique_files = list(set(files_to_instrument))
      relativize_paths(unique_files, self._settings.workdir)

      args = [
        '--basedir',
        self._settings.workdir,
        '--datafile',
        self._canonical_datafile,
      ]

      if self._include_user_classpath:
        closure = BuildGraph.closure(self._targets, bfs=True, include_scopes=Scopes.JVM_TEST_SCOPES,
          respect_intransitive=True)

        aux_classpath = safe_classpath(
          ClasspathUtil.classpath(closure, runtime_classpath),
          synthetic_jar_dir=None)
        args.append('--auxClasspath')
        args.extend(aux_classpath)

      # apply class incl/excl filters
      if len(self._include_classes) > 0:
        for pattern in self._include_classes:
          args += ["--includeClasses", pattern]
      else:
        args += ["--includeClasses", '.*']  # default to instrumenting all classes
      for pattern in self._exclude_classes:
        args += ["--excludeClasses", pattern]

      with temporary_file() as tmp_file:
        tmp_file.write("\n".join(unique_files))
        tmp_file.flush()

        args += ["--listOfFilesToInstrument", tmp_file.name]

        main = 'net.sourceforge.cobertura.instrument.InstrumentMain'
        self._settings.log.debug(
          "executing cobertura instrumentation with the following args: {}".format(args))
        result = self._execute_java(classpath=cobertura_cp,
                                    main=main,
                                    jvm_options=self._settings.coverage_jvm_options,
                                    args=args,
                                    workunit_factory=self._context.new_workunit,
                                    workunit_name='cobertura-instrument')
        if result != 0:
          raise TaskError("java {0} ... exited non-zero ({1})"
                          " 'failed to instrument'".format(main, result))
Exemple #28
0
 def reset_build_graph(self):
   """Start over with a fresh build graph with no targets in it."""
   self.address_mapper = BuildFileAddressMapper(self.build_file_parser, FilesystemBuildFile)
   self.build_graph = BuildGraph(address_mapper=self.address_mapper)
Exemple #29
0
class BaseTest(unittest.TestCase):
  """A baseclass useful for tests requiring a temporary buildroot."""

  def build_path(self, relpath):
    """Returns the canonical BUILD file path for the given relative build path."""
    if os.path.basename(relpath).startswith('BUILD'):
      return relpath
    else:
      return os.path.join(relpath, 'BUILD')

  def create_dir(self, relpath):
    """Creates a directory under the buildroot.

    relpath: The relative path to the directory from the build root.
    """
    path = os.path.join(self.build_root, relpath)
    safe_mkdir(path)
    return path

  def create_workdir_dir(self, relpath):
    """Creates a directory under the work directory.

    relpath: The relative path to the directory from the work directory.
    """
    path = os.path.join(self.pants_workdir, relpath)
    safe_mkdir(path)
    return path

  def create_file(self, relpath, contents='', mode='wb'):
    """Writes to a file under the buildroot.

    relpath:  The relative path to the file from the build root.
    contents: A string containing the contents of the file - '' by default..
    mode:     The mode to write to the file in - over-write by default.
    """
    path = os.path.join(self.build_root, relpath)
    with safe_open(path, mode=mode) as fp:
      fp.write(contents)
    return path

  def create_workdir_file(self, relpath, contents='', mode='wb'):
    """Writes to a file under the work directory.

    relpath:  The relative path to the file from the work directory.
    contents: A string containing the contents of the file - '' by default..
    mode:     The mode to write to the file in - over-write by default.
    """
    path = os.path.join(self.pants_workdir, relpath)
    with safe_open(path, mode=mode) as fp:
      fp.write(contents)
    return path

  def add_to_build_file(self, relpath, target):
    """Adds the given target specification to the BUILD file at relpath.

    relpath: The relative path to the BUILD file from the build root.
    target:  A string containing the target definition as it would appear in a BUILD file.
    """
    self.create_file(self.build_path(relpath), target, mode='a')
    cls = self.address_mapper._build_file_type
    return cls(root_dir=self.build_root, relpath=self.build_path(relpath))

  def make_target(self,
                  spec='',
                  target_type=Target,
                  dependencies=None,
                  derived_from=None,
                  **kwargs):
    """Creates a target and injects it into the test's build graph.

    :param string spec: The target address spec that locates this target.
    :param type target_type: The concrete target subclass to create this new target from.
    :param list dependencies: A list of target instances this new target depends on.
    :param derived_from: The target this new target was derived from.
    :type derived_from: :class:`pants.build_graph.target.Target`
    """
    address = Address.parse(spec)
    target = target_type(name=address.target_name,
                         address=address,
                         build_graph=self.build_graph,
                         **kwargs)
    dependencies = dependencies or []

    self.build_graph.inject_target(target,
                                   dependencies=[dep.address for dep in dependencies],
                                   derived_from=derived_from)

    # TODO(John Sirois): This re-creates a little bit too much work done by the BuildGraph.
    # Fixup the BuildGraph to deal with non BuildFileAddresses better and just leverage it.
    for traversable_dependency_spec in target.traversable_dependency_specs:
      traversable_dependency_address = Address.parse(traversable_dependency_spec,
                                                     relative_to=address.spec_path)
      traversable_dependency_target = self.build_graph.get_target(traversable_dependency_address)
      if not traversable_dependency_target:
        raise ValueError('Tests must make targets for traversable dependency specs ahead of them '
                         'being traversed, {} tried to traverse {} which does not exist.'
                         .format(target, traversable_dependency_address))
      if traversable_dependency_target not in target.dependencies:
        self.build_graph.inject_dependency(dependent=target.address,
                                           dependency=traversable_dependency_address)
        target.mark_transitive_invalidation_hash_dirty()

    return target

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

  def setUp(self):
    super(BaseTest, self).setUp()
    Goal.clear()
    Subsystem.reset()

    self.real_build_root = BuildRoot().path

    self.build_root = os.path.realpath(mkdtemp(suffix='_BUILD_ROOT'))
    self.addCleanup(safe_rmtree, self.build_root)

    self.pants_workdir = os.path.join(self.build_root, '.pants.d')
    safe_mkdir(self.pants_workdir)

    self.options = defaultdict(dict)  # scope -> key-value mapping.
    self.options[''] = {
      'pants_workdir': self.pants_workdir,
      'pants_supportdir': os.path.join(self.build_root, 'build-support'),
      'pants_distdir': os.path.join(self.build_root, 'dist'),
      'pants_configdir': os.path.join(self.build_root, 'config'),
      'cache_key_gen_version': '0-test',
    }

    BuildRoot().path = self.build_root
    self.addCleanup(BuildRoot().reset)

    # We need a pants.ini, even if empty. get_buildroot() uses its presence.
    self.create_file('pants.ini')
    self._build_configuration = BuildConfiguration()
    self._build_configuration.register_aliases(self.alias_groups)
    self.build_file_parser = BuildFileParser(self._build_configuration, self.build_root)
    self.address_mapper = BuildFileAddressMapper(self.build_file_parser, FilesystemBuildFile)
    self.build_graph = BuildGraph(address_mapper=self.address_mapper)

  def buildroot_files(self, relpath=None):
    """Returns the set of all files under the test build root.

    :param string relpath: If supplied, only collect files from this subtree.
    :returns: All file paths found.
    :rtype: set
    """
    def scan():
      for root, dirs, files in os.walk(os.path.join(self.build_root, relpath or '')):
        for f in files:
          yield os.path.relpath(os.path.join(root, f), self.build_root)
    return set(scan())

  def reset_build_graph(self):
    """Start over with a fresh build graph with no targets in it."""
    self.address_mapper = BuildFileAddressMapper(self.build_file_parser, FilesystemBuildFile)
    self.build_graph = BuildGraph(address_mapper=self.address_mapper)

  def set_options_for_scope(self, scope, **kwargs):
    self.options[scope].update(kwargs)

  def context(self, for_task_types=None, options=None, passthru_args=None, target_roots=None,
              console_outstream=None, workspace=None, for_subsystems=None):

    # Many tests use source root functionality via the SourceRootConfig.global_instance()
    # (typically accessed via Target.target_base), so we always set it up, for convenience.
    optionables = {SourceRootConfig}
    extra_scopes = set()

    for_subsystems = for_subsystems or ()
    for subsystem in for_subsystems:
      if subsystem.options_scope is None:
        raise TaskError('You must set a scope on your subsystem type before using it in tests.')
      optionables.add(subsystem)

    for_task_types = for_task_types or ()
    for task_type in for_task_types:
      scope = task_type.options_scope
      if scope is None:
        raise TaskError('You must set a scope on your task type before using it in tests.')
      optionables.add(task_type)
      extra_scopes.update([si.scope for si in task_type.known_scope_infos()])
      optionables.update(Subsystem.closure(
        set([dep.subsystem_cls for dep in task_type.subsystem_dependencies_iter()]) |
            self._build_configuration.subsystems()))

    # Now default the option values and override with any caller-specified values.
    # TODO(benjy): Get rid of the options arg, and require tests to call set_options.
    options = options.copy() if options else {}
    for s, opts in self.options.items():
      scoped_opts = options.setdefault(s, {})
      scoped_opts.update(opts)

    options = create_options_for_optionables(optionables,
                                             extra_scopes=extra_scopes,
                                             options=options)
    Subsystem._options = options
    context = create_context(options=options,
                             passthru_args=passthru_args,
                             target_roots=target_roots,
                             build_graph=self.build_graph,
                             build_file_parser=self.build_file_parser,
                             address_mapper=self.address_mapper,
                             console_outstream=console_outstream,
                             workspace=workspace)
    return context

  def tearDown(self):
    super(BaseTest, self).tearDown()
    FilesystemBuildFile.clear_cache()
    Subsystem.reset()

  def target(self, spec):
    """Resolves the given target address to a Target object.

    address: The BUILD target address to resolve.

    Returns the corresponding Target or else None if the address does not point to a defined Target.
    """
    address = Address.parse(spec)
    self.build_graph.inject_address_closure(address)
    return self.build_graph.get_target(address)

  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

  def create_files(self, path, files):
    """Writes to a file under the buildroot with contents same as file name.

     path:  The relative path to the file from the build root.
     files: List of file names.
    """
    for f in files:
      self.create_file(os.path.join(path, f), contents=f)

  def create_library(self, path, target_type, name, sources=None, **kwargs):
    """Creates a library target of given type at the BUILD file at path with sources

     path: The relative path to the BUILD file from the build root.
     target_type: valid pants target type.
     name: Name of the library target.
     sources: List of source file at the path relative to path.
     **kwargs: Optional attributes that can be set for any library target.
       Currently it includes support for resources, java_sources, provides
       and dependencies.
    """
    if sources:
      self.create_files(path, sources)
    self.add_to_build_file(path, dedent('''
          %(target_type)s(name='%(name)s',
            %(sources)s
            %(resources)s
            %(java_sources)s
            %(provides)s
            %(dependencies)s
          )
        ''' % dict(target_type=target_type,
                   name=name,
                   sources=('sources=%s,' % repr(sources)
                              if sources else ''),
                   resources=('resources=["%s"],' % kwargs.get('resources')
                              if 'resources' in kwargs else ''),
                   java_sources=('java_sources=[%s],'
                                 % ','.join(map(lambda str_target: '"%s"' % str_target,
                                                kwargs.get('java_sources')))
                                 if 'java_sources' in kwargs else ''),
                   provides=('provides=%s,' % kwargs.get('provides')
                              if 'provides' in kwargs else ''),
                   dependencies=('dependencies=%s,' % kwargs.get('dependencies')
                              if 'dependencies' in kwargs else ''),
                   )))
    return self.target('%s:%s' % (path, name))

  def create_resources(self, path, name, *sources):
    return self.create_library(path, 'resources', name, sources)

  def assertUnorderedPrefixEqual(self, expected, actual_iter):
    """Consumes len(expected) items from the given iter, and asserts that they match, unordered."""
    actual = list(itertools.islice(actual_iter, len(expected)))
    self.assertEqual(sorted(expected), sorted(actual))

  def assertPrefixEqual(self, expected, actual_iter):
    """Consumes len(expected) items from the given iter, and asserts that they match, in order."""
    self.assertEqual(expected, list(itertools.islice(actual_iter, len(expected))))
Exemple #30
0
    def instrument(self, output_dir):
        for datafile in self._iter_datafiles(output_dir):
            os.unlink(datafile)

        self._canonical_datafile = os.path.join(
            output_dir, '{}.canonical'.format(self._DATAFILE_NAME))
        # It's conceivable we'll be executing a test that has no source file dependencies; ie: we'll
        # never generate a canonical coverage datafile below. Create an empty one here to allow the
        # test run to proceeed normally.
        touch(self._canonical_datafile)

        # Setup an instrumentation classpath based on the existing runtime classpath.
        runtime_classpath = self._context.products.get_data(
            'runtime_classpath')
        instrumentation_classpath = self._context.products.safe_create_data(
            'instrument_classpath', runtime_classpath.copy)
        self.initialize_instrument_classpath(output_dir, self._settings,
                                             self._targets,
                                             instrumentation_classpath)

        cobertura_cp = self._settings.tool_classpath('cobertura-instrument')
        files_to_instrument = []
        for target in self._targets:
            if Cobertura.is_coverage_target(target):
                paths = instrumentation_classpath.get_for_target(target)
                for (name, path) in paths:
                    files_to_instrument.append(path)

        if len(files_to_instrument) > 0:
            unique_files = list(set(files_to_instrument))
            relativize_paths(unique_files, self._settings.workdir)

            args = [
                '--basedir',
                self._settings.workdir,
                '--datafile',
                self._canonical_datafile,
            ]

            if self._include_user_classpath:
                closure = BuildGraph.closure(
                    self._targets,
                    bfs=True,
                    include_scopes=Scopes.JVM_TEST_SCOPES,
                    respect_intransitive=True)

                aux_classpath = safe_classpath(ClasspathUtil.classpath(
                    closure, runtime_classpath),
                                               synthetic_jar_dir=None)
                args.append('--auxClasspath')
                args.extend(aux_classpath)

            # apply class incl/excl filters
            if len(self._include_classes) > 0:
                for pattern in self._include_classes:
                    args += ["--includeClasses", pattern]
            else:
                args += ["--includeClasses",
                         '.*']  # default to instrumenting all classes
            for pattern in self._exclude_classes:
                args += ["--excludeClasses", pattern]

            with temporary_file(binary_mode=False) as tmp_file:
                tmp_file.write("\n".join(unique_files))
                tmp_file.flush()

                args += ["--listOfFilesToInstrument", tmp_file.name]

                main = 'net.sourceforge.cobertura.instrument.InstrumentMain'
                self._settings.log.debug(
                    "executing cobertura instrumentation with the following args: {}"
                    .format(args))
                result = self._execute_java(
                    classpath=cobertura_cp,
                    main=main,
                    jvm_options=self._settings.coverage_jvm_options,
                    args=args,
                    workunit_factory=self._context.new_workunit,
                    workunit_name='cobertura-instrument')
                if result != 0:
                    raise TaskError("java {0} ... exited non-zero ({1})"
                                    " 'failed to instrument'".format(
                                        main, result))