diff --git a/src/python/pants/backend/codegen/avro/java/rules.py b/src/python/pants/backend/codegen/avro/java/rules.py index 83746bbdd88..1a761d01297 100644 --- a/src/python/pants/backend/codegen/avro/java/rules.py +++ b/src/python/pants/backend/codegen/avro/java/rules.py @@ -35,7 +35,7 @@ ) from pants.engine.unions import UnionRule from pants.jvm.goals import lockfile -from pants.jvm.goals.lockfile import JvmLockfileRequest +from pants.jvm.goals.lockfile import JvmLockfileRequest, JvmLockfileRequestFromTool from pants.jvm.jdk_rules import JdkSetup from pants.jvm.resolve.coursier_fetch import MaterializedClasspath, MaterializedClasspathRequest from pants.source.source_root import SourceRoot, SourceRootRequest @@ -211,10 +211,9 @@ def make_avro_process( @rule async def generate_avro_tools_lockfile_request( - _: AvroToolLockfileSentinel, - tool: AvroSubsystem, + _: AvroToolLockfileSentinel, tool: AvroSubsystem ) -> JvmLockfileRequest: - return JvmLockfileRequest.from_tool(tool) + return await Get(JvmLockfileRequest, JvmLockfileRequestFromTool(tool)) def rules(): diff --git a/src/python/pants/backend/codegen/protobuf/scala/rules.py b/src/python/pants/backend/codegen/protobuf/scala/rules.py index a305fbba619..529e03ea642 100644 --- a/src/python/pants/backend/codegen/protobuf/scala/rules.py +++ b/src/python/pants/backend/codegen/protobuf/scala/rules.py @@ -45,7 +45,7 @@ from pants.engine.unions import UnionRule from pants.jvm.compile import ClasspathEntry from pants.jvm.goals import lockfile -from pants.jvm.goals.lockfile import JvmLockfileRequest +from pants.jvm.goals.lockfile import JvmLockfileRequest, JvmLockfileRequestFromTool from pants.jvm.jdk_rules import JdkSetup from pants.jvm.resolve.common import ArtifactRequirements, Coordinate from pants.jvm.resolve.coursier_fetch import MaterializedClasspath, MaterializedClasspathRequest @@ -368,10 +368,9 @@ async def setup_scalapb_shim_classfiles( @rule async def generate_scalapbc_lockfile_request( - _: ScalapbcToolLockfileSentinel, - tool: ScalaPBSubsystem, + _: ScalapbcToolLockfileSentinel, tool: ScalaPBSubsystem ) -> JvmLockfileRequest: - return JvmLockfileRequest.from_tool(tool) + return await Get(JvmLockfileRequest, JvmLockfileRequestFromTool(tool)) def rules(): diff --git a/src/python/pants/backend/codegen/thrift/scrooge/rules.py b/src/python/pants/backend/codegen/thrift/scrooge/rules.py index cd01ad73342..4952b8c0cf3 100644 --- a/src/python/pants/backend/codegen/thrift/scrooge/rules.py +++ b/src/python/pants/backend/codegen/thrift/scrooge/rules.py @@ -16,7 +16,7 @@ from pants.engine.target import TransitiveTargets, TransitiveTargetsRequest, WrappedTarget from pants.engine.unions import UnionRule from pants.jvm.goals import lockfile -from pants.jvm.goals.lockfile import JvmLockfileRequest +from pants.jvm.goals.lockfile import JvmLockfileRequest, JvmLockfileRequestFromTool from pants.jvm.jdk_rules import JdkSetup from pants.jvm.resolve.coursier_fetch import MaterializedClasspath, MaterializedClasspathRequest from pants.source.source_root import SourceRootsRequest, SourceRootsResult @@ -135,10 +135,9 @@ async def generate_scrooge_thrift_sources( @rule async def generate_scrooge_lockfile_request( - _: ScroogeToolLockfileSentinel, - scrooge: ScroogeSubsystem, + _: ScroogeToolLockfileSentinel, scrooge: ScroogeSubsystem ) -> JvmLockfileRequest: - return JvmLockfileRequest.from_tool(scrooge) + return await Get(JvmLockfileRequest, JvmLockfileRequestFromTool(scrooge)) def rules(): diff --git a/src/python/pants/backend/java/lint/google_java_format/rules.py b/src/python/pants/backend/java/lint/google_java_format/rules.py index f32babdc383..51938f6e5cb 100644 --- a/src/python/pants/backend/java/lint/google_java_format/rules.py +++ b/src/python/pants/backend/java/lint/google_java_format/rules.py @@ -17,7 +17,7 @@ from pants.engine.target import FieldSet, Target from pants.engine.unions import UnionRule from pants.jvm.goals import lockfile -from pants.jvm.goals.lockfile import JvmLockfileRequest +from pants.jvm.goals.lockfile import JvmLockfileRequest, JvmLockfileRequestFromTool from pants.jvm.jdk_rules import JdkSetup from pants.jvm.resolve.coursier_fetch import MaterializedClasspath, MaterializedClasspathRequest from pants.util.logging import LogLevel @@ -158,10 +158,9 @@ async def google_java_format_lint( @rule async def generate_google_java_format_lockfile_request( - _: GoogleJavaFormatToolLockfileSentinel, - tool: GoogleJavaFormatSubsystem, + _: GoogleJavaFormatToolLockfileSentinel, tool: GoogleJavaFormatSubsystem ) -> JvmLockfileRequest: - return JvmLockfileRequest.from_tool(tool) + return await Get(JvmLockfileRequest, JvmLockfileRequestFromTool(tool)) def rules(): diff --git a/src/python/pants/backend/scala/compile/scalac_plugins.py b/src/python/pants/backend/scala/compile/scalac_plugins.py index d4058e557da..d1ff2da73d1 100644 --- a/src/python/pants/backend/scala/compile/scalac_plugins.py +++ b/src/python/pants/backend/scala/compile/scalac_plugins.py @@ -19,11 +19,13 @@ from pants.engine.unions import UnionRule from pants.jvm.goals import lockfile from pants.jvm.goals.lockfile import JvmLockfileRequest +from pants.jvm.resolve.common import ArtifactRequirements from pants.jvm.resolve.coursier_fetch import ( CoursierResolvedLockfile, MaterializedClasspath, MaterializedClasspathRequest, ) +from pants.jvm.resolve.jvm_tool import GatherJvmCoordinatesRequest from pants.jvm.resolve.jvm_tool import rules as jvm_tool_rules from pants.util.ordered_set import FrozenOrderedSet from pants.util.strutil import bullet_list @@ -55,7 +57,7 @@ async def parse_global_scalac_plugins(scalac_plugins: Scalac) -> _LoadedGlobalSc if invalid_targets: raise ValueError( - f"The `[{Scalac.options_scope}].global` option accepts only " + f"The `[{Scalac.options_scope}].plugins_global` option accepts only " f"`{ScalacPluginTarget.alias}` targets, but got:\n\n" f"{bullet_list(type(t).alias for t in invalid_targets)}" ) @@ -70,13 +72,20 @@ class GlobalScalacPluginsToolLockfileSentinel(ToolLockfileSentinel): @rule -def generate_global_scalac_plugins_lockfile_request( +async def generate_global_scalac_plugins_lockfile_request( _: GlobalScalacPluginsToolLockfileSentinel, loaded_global_plugins: _LoadedGlobalScalacPlugins, scalac_plugins: Scalac, ) -> JvmLockfileRequest: + artifacts = await Get( + ArtifactRequirements, + GatherJvmCoordinatesRequest( + FrozenOrderedSet(loaded_global_plugins.artifact_address_inputs), + f"[{scalac_plugins.options_scope}].plugins_global", + ), + ) return JvmLockfileRequest( - artifact_inputs=FrozenOrderedSet(loaded_global_plugins.artifact_address_inputs), + artifacts=artifacts, resolve_name="scalac-plugins", lockfile_dest=scalac_plugins.plugins_global_lockfile, ) diff --git a/src/python/pants/backend/scala/compile/scalac_test.py b/src/python/pants/backend/scala/compile/scalac_test.py index 6405a0199e5..20249504590 100644 --- a/src/python/pants/backend/scala/compile/scalac_test.py +++ b/src/python/pants/backend/scala/compile/scalac_test.py @@ -384,7 +384,8 @@ def test_compile_with_scalac_plugin(rule_runner: RuleRunner) -> None: scalac_plugin( name = "acyclic", - artifact = ":acyclic_lib", + # TODO: Support relative addresses. + artifact = "lib:acyclic_lib", ) scala_sources( @@ -468,7 +469,8 @@ def test_compile_with_multiple_scalac_plugins(rule_runner: RuleRunner) -> None: scalac_plugin( name="kind-projector", plugin_name="kind-projector", - artifact=":kind-projector-lib", + # TODO: Support relative addresses. + artifact="lib:kind-projector-lib", ) jvm_artifact( @@ -481,7 +483,8 @@ def test_compile_with_multiple_scalac_plugins(rule_runner: RuleRunner) -> None: scalac_plugin( name="better-monadic-for", plugin_name="bm4", - artifact=":better-monadic-for-lib", + # TODO: Support relative addresses. + artifact="lib:better-monadic-for-lib", ) """ ), diff --git a/src/python/pants/backend/scala/lint/scalafmt/rules.py b/src/python/pants/backend/scala/lint/scalafmt/rules.py index 674f9faf25c..af193f86d79 100644 --- a/src/python/pants/backend/scala/lint/scalafmt/rules.py +++ b/src/python/pants/backend/scala/lint/scalafmt/rules.py @@ -30,7 +30,7 @@ from pants.engine.target import FieldSet, Target from pants.engine.unions import UnionRule from pants.jvm.goals import lockfile -from pants.jvm.goals.lockfile import JvmLockfileRequest +from pants.jvm.goals.lockfile import JvmLockfileRequest, JvmLockfileRequestFromTool from pants.jvm.jdk_rules import JdkSetup from pants.jvm.resolve.coursier_fetch import MaterializedClasspath, MaterializedClasspathRequest from pants.util.frozendict import FrozenDict @@ -318,10 +318,9 @@ async def scalafmt_lint(field_sets: ScalafmtRequest, tool: ScalafmtSubsystem) -> @rule async def generate_scalafmt_lockfile_request( - _: ScalafmtToolLockfileSentinel, - tool: ScalafmtSubsystem, + _: ScalafmtToolLockfileSentinel, tool: ScalafmtSubsystem ) -> JvmLockfileRequest: - return JvmLockfileRequest.from_tool(tool) + return await Get(JvmLockfileRequest, JvmLockfileRequestFromTool(tool)) def rules(): diff --git a/src/python/pants/backend/scala/test/scalatest.py b/src/python/pants/backend/scala/test/scalatest.py index b9ef66d3ae4..e4a53c95ccc 100644 --- a/src/python/pants/backend/scala/test/scalatest.py +++ b/src/python/pants/backend/scala/test/scalatest.py @@ -22,7 +22,7 @@ from pants.engine.unions import UnionRule from pants.jvm.classpath import Classpath from pants.jvm.goals import lockfile -from pants.jvm.goals.lockfile import JvmLockfileRequest +from pants.jvm.goals.lockfile import JvmLockfileRequest, JvmLockfileRequestFromTool from pants.jvm.jdk_rules import JdkSetup from pants.jvm.resolve.coursier_fetch import MaterializedClasspath, MaterializedClasspathRequest from pants.jvm.subsystems import JvmSubsystem @@ -164,7 +164,7 @@ async def setup_scalatest_debug_request(field_set: ScalatestTestFieldSet) -> Tes async def generate_scalatest_lockfile_request( _: ScalatestToolLockfileSentinel, scalatest: Scalatest ) -> JvmLockfileRequest: - return JvmLockfileRequest.from_tool(scalatest) + return await Get(JvmLockfileRequest, JvmLockfileRequestFromTool(scalatest)) def rules(): diff --git a/src/python/pants/init/BUILD b/src/python/pants/init/BUILD index f5b66f8f9fb..a0a17ebac69 100644 --- a/src/python/pants/init/BUILD +++ b/src/python/pants/init/BUILD @@ -50,4 +50,4 @@ target( ], ) -python_tests(name="tests", timeout=200) +python_tests(name="tests", timeout=240) diff --git a/src/python/pants/jvm/goals/lockfile.py b/src/python/pants/jvm/goals/lockfile.py index 16e43ecceb0..e14574cc615 100644 --- a/src/python/pants/jvm/goals/lockfile.py +++ b/src/python/pants/jvm/goals/lockfile.py @@ -20,20 +20,45 @@ from pants.jvm.resolve.jvm_tool import GatherJvmCoordinatesRequest, JvmToolBase from pants.jvm.resolve.key import CoursierResolveKey from pants.util.logging import LogLevel +from pants.util.meta import frozen_after_init from pants.util.ordered_set import FrozenOrderedSet @dataclass(frozen=True) class JvmLockfileRequest(LockfileRequest): + artifacts: ArtifactRequirements + + +@frozen_after_init +@dataclass(unsafe_hash=True) +class JvmLockfileRequestFromTool: artifact_inputs: FrozenOrderedSet[str] + options_scope: str + lockfile_dest: str - @classmethod - def from_tool(cls, tool: JvmToolBase) -> JvmLockfileRequest: - return cls( - artifact_inputs=FrozenOrderedSet(tool.artifact_inputs), - resolve_name=tool.options_scope, - lockfile_dest=tool.lockfile, - ) + def __init__(self, tool: JvmToolBase) -> None: + # Note that `JvmToolBase` is not hashable, so we extract the relevant information eagerly. + self.artifact_inputs = FrozenOrderedSet(tool.artifact_inputs) + self.options_scope = tool.options_scope + self.lockfile_dest = tool.lockfile + + +@rule +async def setup_lockfile_request_from_tool( + request: JvmLockfileRequestFromTool, +) -> JvmLockfileRequest: + artifacts = await Get( + ArtifactRequirements, + GatherJvmCoordinatesRequest( + request.artifact_inputs, + f"[{request.options_scope}].artifacts", + ), + ) + return JvmLockfileRequest( + artifacts=artifacts, + resolve_name=request.options_scope, + lockfile_dest=request.lockfile_dest, + ) @rule @@ -45,11 +70,7 @@ def wrap_python_lockfile_request(request: JvmLockfileRequest) -> WrappedLockfile async def generate_jvm_lockfile( request: JvmLockfileRequest, ) -> Lockfile: - requirements = await Get( - ArtifactRequirements, - GatherJvmCoordinatesRequest(request.artifact_inputs, f"[{request.resolve_name}].artifacts"), - ) - resolved_lockfile = await Get(CoursierResolvedLockfile, ArtifactRequirements, requirements) + resolved_lockfile = await Get(CoursierResolvedLockfile, ArtifactRequirements, request.artifacts) lockfile_digest = await Get( Digest, CreateDigest([FileContent(request.lockfile_dest, resolved_lockfile.to_serialized())]), @@ -62,13 +83,13 @@ async def load_jvm_lockfile( request: JvmLockfileRequest, ) -> CoursierResolvedLockfile: """Loads an existing lockfile from disk.""" - if not request.artifact_inputs: + if not request.artifacts: return CoursierResolvedLockfile(entries=()) lockfile_snapshot = await Get(Snapshot, PathGlobs([request.lockfile_dest])) if not lockfile_snapshot.files: raise ValueError( - f"JVM tool `{request.resolve_name}` does not have a lockfile generated. " + f"JVM resolve `{request.resolve_name}` does not have a lockfile generated. " f"Run `{GenerateLockfilesSubsystem.name} --resolve={request.resolve_name} to " "generate it." ) diff --git a/src/python/pants/jvm/resolve/common.py b/src/python/pants/jvm/resolve/common.py index 21d88e613df..86328487eaf 100644 --- a/src/python/pants/jvm/resolve/common.py +++ b/src/python/pants/jvm/resolve/common.py @@ -27,7 +27,7 @@ def __init__(self, coords: str) -> None: super().__init__(f"Received invalid artifact coordinates: {coords}") -@dataclass(frozen=True) +@dataclass(frozen=True, order=True) class Coordinate: """A single Maven-style coordinate for a JVM dependency. diff --git a/src/python/pants/jvm/resolve/jvm_tool_test.py b/src/python/pants/jvm/resolve/jvm_tool_test.py index 0969d9184c3..324c996f6e5 100644 --- a/src/python/pants/jvm/resolve/jvm_tool_test.py +++ b/src/python/pants/jvm/resolve/jvm_tool_test.py @@ -9,13 +9,14 @@ from pants.core.util_rules import config_files, source_files from pants.core.util_rules.external_tool import rules as external_tool_rules from pants.engine.fs import Digest, DigestContents -from pants.engine.rules import SubsystemRule, rule -from pants.jvm.goals.lockfile import JvmLockfileRequest +from pants.engine.rules import Get, SubsystemRule, rule +from pants.jvm.goals.lockfile import JvmLockfileRequest, JvmLockfileRequestFromTool +from pants.jvm.goals.lockfile import rules as lockfile_rules from pants.jvm.resolve import jvm_tool -from pants.jvm.resolve.common import ArtifactRequirements, Coordinate +from pants.jvm.resolve.common import Coordinate from pants.jvm.resolve.coursier_fetch import rules as coursier_fetch_rules from pants.jvm.resolve.coursier_setup import rules as coursier_setup_rules -from pants.jvm.resolve.jvm_tool import GatherJvmCoordinatesRequest, JvmToolBase +from pants.jvm.resolve.jvm_tool import JvmToolBase from pants.jvm.target_types import JvmArtifactTarget from pants.jvm.util_rules import rules as util_rules from pants.testutil.rule_runner import PYTHON_BOOTSTRAP_ENV, QueryRule, RuleRunner @@ -39,7 +40,7 @@ class MockJvmToolLockfileSentinel(ToolLockfileSentinel): async def generate_test_tool_lockfile_request( _: MockJvmToolLockfileSentinel, tool: MockJvmTool ) -> JvmLockfileRequest: - return JvmLockfileRequest.from_tool(tool) + return await Get(JvmLockfileRequest, JvmLockfileRequestFromTool(tool)) def test_jvm_tool_base_extracts_correct_coordinates() -> None: @@ -52,10 +53,10 @@ def test_jvm_tool_base_extracts_correct_coordinates() -> None: *source_files.rules(), *util_rules(), *jvm_tool.rules(), + *lockfile_rules(), generate_test_tool_lockfile_request, SubsystemRule(MockJvmTool), QueryRule(JvmLockfileRequest, (MockJvmToolLockfileSentinel,)), - QueryRule(ArtifactRequirements, (GatherJvmCoordinatesRequest,)), QueryRule(DigestContents, (Digest,)), ], target_types=[JvmArtifactTarget], @@ -83,16 +84,8 @@ def test_jvm_tool_base_extracts_correct_coordinates() -> None: } ) lockfile_request = rule_runner.request(JvmLockfileRequest, [MockJvmToolLockfileSentinel()]) - assert sorted(lockfile_request.artifact_inputs) == [ - "//:junit_junit", - "org.hamcrest:hamcrest-core:1.3", - ] - - requirements = rule_runner.request( - ArtifactRequirements, [GatherJvmCoordinatesRequest(lockfile_request.artifact_inputs, "")] - ) - coordinates = [i.coordinate for i in requirements] - assert sorted(coordinates, key=lambda c: (c.group, c.artifact, c.version)) == [ + coordinates = sorted(i.coordinate for i in lockfile_request.artifacts) + assert coordinates == [ Coordinate(group="junit", artifact="junit", version="4.13.2"), Coordinate(group="org.hamcrest", artifact="hamcrest-core", version="1.3"), ] diff --git a/src/python/pants/jvm/test/junit.py b/src/python/pants/jvm/test/junit.py index 378adf80be8..8472e3970da 100644 --- a/src/python/pants/jvm/test/junit.py +++ b/src/python/pants/jvm/test/junit.py @@ -21,7 +21,7 @@ from pants.engine.unions import UnionRule from pants.jvm.classpath import Classpath from pants.jvm.goals import lockfile -from pants.jvm.goals.lockfile import JvmLockfileRequest +from pants.jvm.goals.lockfile import JvmLockfileRequest, JvmLockfileRequestFromTool from pants.jvm.jdk_rules import JdkSetup from pants.jvm.resolve.coursier_fetch import MaterializedClasspath, MaterializedClasspathRequest from pants.jvm.subsystems import JvmSubsystem @@ -161,7 +161,7 @@ async def setup_junit_debug_request(field_set: JunitTestFieldSet) -> TestDebugRe async def generate_junit_lockfile_request( _: JunitToolLockfileSentinel, junit: JUnit ) -> JvmLockfileRequest: - return JvmLockfileRequest.from_tool(junit) + return await Get(JvmLockfileRequest, JvmLockfileRequestFromTool(junit)) def rules():