Skip to content

Commit

Permalink
accept target addresses in addition to coordinate strings
Browse files Browse the repository at this point in the history
[ci skip-rust]

[ci skip-build-wheels]
  • Loading branch information
Tom Dyas committed Dec 3, 2021
1 parent 23d1d7b commit d689c16
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 37 deletions.
4 changes: 2 additions & 2 deletions src/python/pants/backend/java/subsystems/junit.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ class JUnit(JvmToolBase):
default_version = "5.7.2"
default_artifacts = [
"org.junit.platform:junit-platform-console:1.7.2",
"org.junit.jupiter:junit-jupiter-engine:%VERSION%",
"org.junit.vintage:junit-vintage-engine:%VERSION%",
"org.junit.jupiter:junit-jupiter-engine:{version}",
"org.junit.vintage:junit-vintage-engine:{version}",
]
default_lockfile_resource = ("pants.backend.java.test", "junit.default.lockfile.txt")
default_lockfile_path = "src/python/pants/backend/java/test/junit.default.lockfile.txt"
Expand Down
8 changes: 6 additions & 2 deletions src/python/pants/build_graph/address.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@
BANNED_CHARS_IN_GENERATED_NAME = frozenset(r":#!@?=")


class InvalidSpecPath(ValueError):
class AddressInputParseException(ValueError):
"""Base class for parse exceptions raised by AddressInput."""


class InvalidSpecPath(AddressInputParseException):
"""Indicate an invalid spec path for `Address`."""


class InvalidTargetName(ValueError):
class InvalidTargetName(AddressInputParseException):
"""Indicate an invalid target name for `Address`."""


Expand Down
27 changes: 2 additions & 25 deletions src/python/pants/jvm/goals/coursier.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from itertools import groupby

from pants.base.glob_match_error_behavior import GlobMatchErrorBehavior
from pants.engine.addresses import Addresses, UnparsedAddressInputs
from pants.engine.console import Console
from pants.engine.fs import (
EMPTY_DIGEST,
Expand Down Expand Up @@ -43,9 +42,7 @@
JvmArtifactGroupField,
JvmArtifactVersionField,
JvmCompatibleResolveNamesField,
JvmRequirementsField,
)
from pants.util.logging import LogLevel


class CoursierResolveSubsystem(GoalSubsystem):
Expand All @@ -68,27 +65,7 @@ class CoursierResolve(Goal):
subsystem_cls = CoursierResolveSubsystem


@dataclass(frozen=True)
class GatherArtifactRequirementsRequest:
"""Gather the coordinate requirements from a JarRequirementsField."""

requirements: JvmRequirementsField


@rule(level=LogLevel.DEBUG)
async def gather_artifact_requirements(
request: GatherArtifactRequirementsRequest,
) -> ArtifactRequirements:

requirements_addresses = await Get(
Addresses, UnparsedAddressInputs, request.requirements.to_unparsed_address_inputs()
)
requirements_targets = await Get(Targets, Addresses, requirements_addresses)

return ArtifactRequirements(_coordinate_from_target(tgt) for tgt in requirements_targets)


def _coordinate_from_target(tgt: Target) -> Coordinate:
def coordinate_from_target(tgt: Target) -> Coordinate:
group = tgt[JvmArtifactGroupField].value
if not group:
raise InvalidTargetException(
Expand Down Expand Up @@ -199,7 +176,7 @@ async def coursier_generate_lockfile(
]

artifact_requirements = ArtifactRequirements(
[_coordinate_from_target(tgt) for tgt in resolvable_dependencies]
[coordinate_from_target(tgt) for tgt in resolvable_dependencies]
)

resolved_lockfile = await Get(
Expand Down
63 changes: 55 additions & 8 deletions src/python/pants/jvm/resolve/jvm_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,21 @@
from typing import ClassVar, Iterable, Sequence, cast

from pants.backend.python.target_types import UnrecognizedResolveNamesError
from pants.build_graph.address import Address, AddressInput, AddressInputParseException
from pants.engine.addresses import Addresses
from pants.engine.fs import CreateDigest, Digest, FileContent, MergeDigests, Workspace
from pants.engine.goal import Goal, GoalSubsystem
from pants.engine.internals.selectors import Get, MultiGet
from pants.engine.rules import collect_rules, goal_rule, rule
from pants.engine.target import Targets
from pants.engine.unions import UnionMembership, union
from pants.jvm.goals.coursier import coordinate_from_target
from pants.jvm.resolve.coursier_fetch import (
ArtifactRequirements,
Coordinate,
CoursierResolvedLockfile,
)
from pants.jvm.target_types import JvmArtifactFieldSet
from pants.option.subsystem import Subsystem
from pants.util.logging import LogLevel
from pants.util.ordered_set import FrozenOrderedSet
Expand Down Expand Up @@ -93,11 +98,8 @@ def version(self) -> str:
return cast(str, self.options.version)

@property
def artifacts(self) -> tuple[Coordinate, ...]:
return tuple(
Coordinate.from_coord_str(s.replace("%VERSION%", self.version))
for s in self.options.artifacts
)
def artifact_inputs(self) -> tuple[str, ...]:
return tuple(s.format(version=self.version) for s in self.options.artifacts)

@property
def lockfile(self) -> str:
Expand Down Expand Up @@ -128,14 +130,14 @@ class JvmToolLockfileSentinel:

@dataclass(frozen=True)
class JvmToolLockfileRequest:
artifacts: FrozenOrderedSet[Coordinate]
artifact_inputs: FrozenOrderedSet[str]
resolve_name: str
lockfile_dest: str

@classmethod
def from_tool(cls, tool: JvmToolBase) -> JvmToolLockfileRequest:
return cls(
artifacts=FrozenOrderedSet(tool.artifacts),
artifact_inputs=FrozenOrderedSet(tool.artifact_inputs),
resolve_name=tool.options_scope,
lockfile_dest=tool.lockfile,
)
Expand Down Expand Up @@ -219,7 +221,52 @@ async def generate_lockfiles_goal(
async def generate_jvm_lockfile(
request: JvmToolLockfileRequest,
) -> JvmToolLockfile:
resolved_lockfile = await Get(CoursierResolvedLockfile, ArtifactRequirements(request.artifacts))
# Separate `artifact_inputs` by whether the strings parse as an `Address` or not.
coordinates: set[Coordinate] = set()
candidate_address_inputs: set[AddressInput] = set()
bad_artifact_inputs = []
for artifact_input in request.artifact_inputs:
# Try parsing as a `Coordinate` first since otherwise `AddressInput.parse` will try to see if the
# group name is a file on disk.
if 2 <= artifact_input.count(":") <= 3:
try:
maybe_coord = Coordinate.from_coord_str(artifact_input)
coordinates.add(maybe_coord)
continue
except Exception:
pass

try:
address_input = AddressInput.parse(artifact_input)
candidate_address_inputs.add(address_input)
except AddressInputParseException:
bad_artifact_inputs.append(artifact_input)

if bad_artifact_inputs:
raise ValueError(
"The following values could not be parsed as an address nor as a JVM string. "
f"The problematic inputs supplied to the `--{request.resolve_name}-artifacts option were: "
f"{', '.join(bad_artifact_inputs)}."
)

# Gather coordinates from the provided addresses.
addresses = await MultiGet(Get(Address, AddressInput, ai) for ai in candidate_address_inputs)
all_supplied_targets = await Get(Targets, Addresses(addresses))
other_targets = []
for tgt in all_supplied_targets:
if JvmArtifactFieldSet.is_applicable(tgt):
coordinates.add(coordinate_from_target(tgt))
else:
other_targets.append(tgt)

if other_targets:
raise ValueError(
"The following addresses reference targets that are not `jvm_artifact` targets. "
f"Please only supply the addresses of `jvm_artifact` for the `--{request.resolve_name}-artifacts "
f"option. The problematic addresses are: {', '.join(str(tgt.address) for tgt in other_targets)}."
)

resolved_lockfile = await Get(CoursierResolvedLockfile, ArtifactRequirements(coordinates))
lockfile_content = resolved_lockfile.to_json()
lockfile_digest = await Get(
Digest, CreateDigest([FileContent(request.lockfile_dest, lockfile_content)])
Expand Down

0 comments on commit d689c16

Please sign in to comment.