Repository URL to install this package:
|
Version:
1.26.0.dev0+gite506aa5f ▾
|
# 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)