Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
pantsbuild.pants / backend / jvm / tasks / classpath_util.py
Size: Mime:
# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

import os
from collections import OrderedDict

from twitter.common.collections import OrderedSet

from pants.backend.jvm.tasks.classpath_entry import ClasspathEntry
from pants.util.contextutil import open_zip
from pants.util.dirutil import fast_relpath, safe_walk
from pants.util.strutil import ensure_text


class ClasspathUtil:
    @classmethod
    def compute_classpath_entries(cls, targets, classpath_products, extra_classpath_tuples, confs):
        """Return the list of classpath entries for a classpath covering the passed targets.

        Filters and adds paths from extra_classpath_tuples to the end of the resulting list.

        :param targets: The targets to generate a classpath for.
        :param ClasspathProducts classpath_products: Product containing classpath elements.
        :param extra_classpath_tuples: Additional classpath entries as tuples of
          (string, ClasspathEntry).
        :param confs: The list of confs for use by this classpath.
        :returns: The classpath entries as a list of path elements.
        :rtype: list of ClasspathEntry
        """
        classpath_iter = cls._classpath_iter(
            classpath_products.get_classpath_entries_for_targets(targets), confs=confs,
        )
        total_classpath = OrderedSet(classpath_iter)

        filtered_extra_classpath_iter = cls._filtered_classpath_by_confs_iter(
            extra_classpath_tuples, confs,
        )
        extra_classpath_iter = cls._entries_iter(filtered_extra_classpath_iter)
        total_classpath.update(extra_classpath_iter)
        return list(total_classpath)

    @classmethod
    def compute_classpath(cls, targets, classpath_products, extra_classpath_tuples, confs):
        """Return the list of classpath entries for a classpath covering the passed targets.

        Filters and adds paths from extra_classpath_tuples to the end of the resulting list.

        As compute_classpath_entries but expects and returns strings, not ClasspathEntries.
        """
        return list(
            entry.path
            for entry in cls.compute_classpath_entries(
                targets,
                classpath_products,
                ((scope, ClasspathEntry(path)) for scope, path in extra_classpath_tuples),
                confs,
            )
        )

    @classmethod
    def classpath(cls, targets, classpath_products, confs=("default",)):
        """Return the classpath as a list of paths covering all the passed targets.

        :param targets: Targets to build an aggregated classpath for.
        :param ClasspathProducts classpath_products: Product containing classpath elements.
        :param confs: The list of confs for use by this classpath.
        :returns: The classpath as a list of path elements.
        :rtype: list of string
        """
        classpath_iter = cls._classpath_iter(
            classpath_products.get_for_targets(targets), confs=confs
        )
        return list(classpath_iter)

    @classmethod
    def _classpath_iter(cls, classpath_entries_for_targets, confs=("default",)):
        filtered_tuples_iter = cls._filtered_classpath_by_confs_iter(
            classpath_entries_for_targets, confs,
        )
        return cls._entries_iter(filtered_tuples_iter)

    @classmethod
    def internal_classpath(cls, targets, classpath_products, confs=("default",)):
        """Return the list of internal classpath entries for a classpath covering all `targets`.

        Any classpath entries contributed by external dependencies will be omitted.

        :param targets: Targets to build an aggregated classpath for.
        :param ClasspathProducts classpath_products: Product containing classpath elements.
        :param confs: The list of confs for use by this classpath.
        :returns: The classpath as a list of path elements.
        :rtype: list of string
        """
        classpath_tuples = classpath_products.get_internal_classpath_entries_for_targets(targets)
        filtered_tuples_iter = cls._filtered_classpath_by_confs_iter(classpath_tuples, confs)
        return [entry.path for entry in cls._entries_iter(filtered_tuples_iter)]

    @classmethod
    def classpath_by_targets(cls, targets, classpath_products, confs=("default",)):
        """Return classpath entries grouped by their targets for the given `targets`.

        :param targets: The targets to lookup classpath products for.
        :param ClasspathProducts classpath_products: Product containing classpath elements.
        :param confs: The list of confs for use by this classpath.
        :returns: The ordered (target, classpath) mappings.
        :rtype: OrderedDict
        """
        classpath_target_tuples = classpath_products.get_product_target_mappings_for_targets(
            targets
        )
        filtered_items_iter = filter(
            cls._accept_conf_filter(confs, lambda x: x[0][0]), classpath_target_tuples
        )

        # group (classpath_entry, target) tuples by targets
        target_to_classpath = OrderedDict()
        for classpath_entry, target in filtered_items_iter:
            _, entry = classpath_entry
            if not target in target_to_classpath:
                target_to_classpath[target] = []
            target_to_classpath[target].append(entry)
        return target_to_classpath

    @classmethod
    def _accept_conf_filter(cls, confs, unpack_func=None):
        def accept_conf_in_item(item):
            conf = unpack_func(item)
            return confs is None or conf in confs

        unpack_func = unpack_func or (lambda x: x)
        return accept_conf_in_item

    @classmethod
    def _filtered_classpath_by_confs_iter(cls, classpath_tuples, confs):
        filter_func = cls._accept_conf_filter(confs, unpack_func=lambda x: x[0])
        return filter(filter_func, classpath_tuples)

    @classmethod
    def _entries_iter(cls, classpath):
        for conf, entry in classpath:
            yield entry

    @classmethod
    def classpath_contents(cls, targets, classpath_products, confs=("default",)):
        """Provide a generator over the contents (classes/resources) of a classpath.

        :param targets: Targets to iterate the contents classpath for.
        :param ClasspathProducts classpath_products: Product containing classpath elements.
        :param confs: The list of confs for use by this classpath.
        :returns: An iterator over all classpath contents, one directory, class or resource relative
                  path per iteration step.
        :rtype: :class:`collections.Iterator` of string
        """
        classpath_iter = cls._classpath_iter(
            classpath_products.get_for_targets(targets), confs=confs,
        )
        for f in cls.classpath_entries_contents(classpath_iter):
            yield f

    @classmethod
    def classpath_entries_contents(cls, classpath_entries):
        """Provide a generator over the contents (classes/resources) of a classpath.

        Subdirectories are included and differentiated via a trailing forward slash (for symmetry
        across ZipFile.namelist and directory walks).

        :param classpath_entries: A sequence of classpath_entries. Non-jars/dirs are ignored.
        :returns: An iterator over all classpath contents, one directory, class or resource relative
                  path per iteration step.
        :rtype: :class:`collections.Iterator` of string
        """
        for entry in classpath_entries:
            if cls.is_jar(entry):
                # Walk the jar namelist.
                with open_zip(entry, mode="r") as jar:
                    for name in jar.namelist():
                        yield ensure_text(name)
            elif os.path.isdir(entry):
                # Walk the directory, including subdirs.
                def rel_walk_name(abs_sub_dir, name):
                    return fast_relpath(os.path.join(abs_sub_dir, name), entry)

                for abs_sub_dir, dirnames, filenames in safe_walk(entry):
                    for name in dirnames:
                        yield f"{rel_walk_name(abs_sub_dir, name)}/"
                    for name in filenames:
                        yield rel_walk_name(abs_sub_dir, name)
            else:
                # non-jar and non-directory classpath entries should be ignored
                pass

    @classmethod
    def classname_for_rel_classfile(cls, class_file_name):
        """Return the class name for the given relative-to-a-classpath-entry file, or None."""
        if not class_file_name.endswith(".class"):
            return None
        return class_file_name[: -len(".class")].replace("/", ".")

    @classmethod
    def is_jar(cls, path):
        """True if the given path represents an existing jar or zip file."""
        return path.endswith((".jar", ".zip")) and os.path.isfile(path)

    @classmethod
    def is_dir(cls, path):
        """True if the given path represents an existing directory."""
        return os.path.isdir(path)