Skip to content

Commit

Permalink
Make JVM sources runnable (#17847)
Browse files Browse the repository at this point in the history
This adds `./pants run`/`experimental_run_in_sandbox` support for more JVM targets without needing to explicitly declare a `deploy_jar`. It does that by adapting the existing `jvm_artifact` rule, which was already fairly generic thanks to the use of `ClasspathEntryRequest` rules.

The rules will attempt to automatically find a `main` method through either a JAR manifest (as before for `jvm_artifact`) or by directly inspecting the contents of the classpath entry JAR returned from compilation. This also permits explicitly declaring a `main` method in case the other approaches are inconclusive.
  • Loading branch information
Christopher Neugebauer authored Dec 22, 2022
1 parent 4beb04e commit 672ca1d
Show file tree
Hide file tree
Showing 11 changed files with 390 additions and 76 deletions.
7 changes: 6 additions & 1 deletion src/python/pants/backend/java/target_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
JunitTestTimeoutField,
JvmDependenciesField,
JvmJdkField,
JvmMainClassNameField,
JvmProvidesTypesField,
JvmResolveField,
JvmRunnableSourceFieldSet,
)


Expand All @@ -36,7 +38,7 @@ class JavaGeneratorSourcesField(MultipleSourcesField):


@dataclass(frozen=True)
class JavaFieldSet(FieldSet):
class JavaFieldSet(JvmRunnableSourceFieldSet):
required_fields = (JavaSourceField,)

sources: JavaSourceField
Expand Down Expand Up @@ -111,6 +113,7 @@ class JavaSourceTarget(Target):
JvmDependenciesField,
JavaSourceField,
JvmResolveField,
JvmMainClassNameField,
JvmProvidesTypesField,
JvmJdkField,
)
Expand All @@ -136,6 +139,7 @@ class JavaSourcesGeneratorTarget(TargetFilesGenerator):
JvmDependenciesField,
JvmResolveField,
JvmJdkField,
JvmMainClassNameField,
JvmProvidesTypesField,
)
help = "Generate a `java_source` target for each file in the `sources` field."
Expand All @@ -145,4 +149,5 @@ def rules():
return [
*collect_rules(),
*jvm_target_types.rules(),
*JavaFieldSet.jvm_rules(),
]
11 changes: 8 additions & 3 deletions src/python/pants/backend/kotlin/target_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from dataclasses import dataclass

from pants.engine.rules import collect_rules
from pants.engine.target import (
COMMON_TARGET_FIELDS,
AsyncFieldMixin,
Expand All @@ -24,8 +23,10 @@
JunitTestSourceField,
JunitTestTimeoutField,
JvmJdkField,
JvmMainClassNameField,
JvmProvidesTypesField,
JvmResolveField,
JvmRunnableSourceFieldSet,
)
from pants.util.strutil import softwrap

Expand Down Expand Up @@ -56,7 +57,7 @@ class KotlincConsumedPluginIdsField(StringSequenceField):


@dataclass(frozen=True)
class KotlinFieldSet(FieldSet):
class KotlinFieldSet(JvmRunnableSourceFieldSet):
required_fields = (KotlinSourceField,)

sources: KotlinSourceField
Expand Down Expand Up @@ -88,6 +89,7 @@ class KotlinSourceTarget(Target):
JvmResolveField,
JvmProvidesTypesField,
JvmJdkField,
JvmMainClassNameField,
)
help = "A single Kotlin source file containing application or library code."

Expand All @@ -113,6 +115,7 @@ class KotlinSourcesGeneratorTarget(TargetFilesGenerator):
JvmResolveField,
JvmJdkField,
JvmProvidesTypesField,
JvmMainClassNameField,
)
help = "Generate a `kotlin_source` target for each file in the `sources` field."

Expand Down Expand Up @@ -232,4 +235,6 @@ class KotlincPluginTarget(Target):


def rules():
return collect_rules()
return [
*KotlinFieldSet.jvm_rules(),
]
3 changes: 2 additions & 1 deletion src/python/pants/backend/scala/compile/scalac.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
from pants.jvm.resolve.coursier_fetch import ToolClasspath, ToolClasspathRequest
from pants.jvm.strip_jar.strip_jar import StripJarRequest
from pants.jvm.subsystems import JvmSubsystem
from pants.jvm.target_types import NO_MAIN_CLASS
from pants.util.logging import LogLevel

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -188,7 +189,7 @@ async def compile_scala_source(
# NB: We set a non-existent main-class so that using `-d` produces a `jar` manifest
# with stable content.
"-Xmain-class",
"no.main.class",
NO_MAIN_CLASS,
"-d",
output_file,
*sorted(
Expand Down
7 changes: 6 additions & 1 deletion src/python/pants/backend/scala/target_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@
JunitTestSourceField,
JunitTestTimeoutField,
JvmJdkField,
JvmMainClassNameField,
JvmProvidesTypesField,
JvmResolveField,
JvmRunnableSourceFieldSet,
)
from pants.util.strutil import softwrap

Expand Down Expand Up @@ -82,7 +84,7 @@ class ScalaConsumedPluginNamesField(StringSequenceField):


@dataclass(frozen=True)
class ScalaFieldSet(FieldSet):
class ScalaFieldSet(JvmRunnableSourceFieldSet):
required_fields = (ScalaSourceField,)

sources: ScalaSourceField
Expand Down Expand Up @@ -258,6 +260,7 @@ class ScalaSourceTarget(Target):
JvmResolveField,
JvmProvidesTypesField,
JvmJdkField,
JvmMainClassNameField,
)
help = "A single Scala source file containing application or library code."

Expand Down Expand Up @@ -305,6 +308,7 @@ class ScalaSourcesGeneratorTarget(TargetFilesGenerator):
ScalaConsumedPluginNamesField,
JvmResolveField,
JvmJdkField,
JvmMainClassNameField,
JvmProvidesTypesField,
)
settings_request_cls = ScalaSettingsRequest
Expand Down Expand Up @@ -359,5 +363,6 @@ def rules():
return (
*collect_rules(),
*jvm_target_types.rules(),
*ScalaFieldSet.jvm_rules(),
UnionRule(TargetFilesGeneratorSettingsRequest, ScalaSettingsRequest),
)
15 changes: 13 additions & 2 deletions src/python/pants/core/goals/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,17 @@
from dataclasses import dataclass
from enum import Enum
from itertools import filterfalse, tee
from typing import Callable, ClassVar, Iterable, Mapping, NamedTuple, Optional, Tuple, TypeVar
from typing import (
Callable,
ClassVar,
Iterable,
Mapping,
NamedTuple,
Optional,
Tuple,
TypeVar,
Union,
)

from typing_extensions import final

Expand All @@ -27,6 +37,7 @@
from pants.engine.rules import (
Effect,
Get,
Rule,
_uncacheable_rule,
collect_rules,
goal_rule,
Expand Down Expand Up @@ -91,7 +102,7 @@ class RunFieldSet(FieldSet, metaclass=ABCMeta):

@final
@classmethod
def rules(cls) -> Iterable:
def rules(cls) -> Iterable[Union[Rule, UnionRule]]:
yield UnionRule(RunFieldSet, cls)
if not cls.supports_debug_adapter:
yield from _unsupported_debug_adapter_rules(cls)
Expand Down
3 changes: 2 additions & 1 deletion src/python/pants/jvm/jvm_common.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Copyright 2022 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).
from pants.jvm import classpath, jdk_rules, resources, run
from pants.jvm import classpath, jdk_rules, resources, run, run_deploy_jar
from pants.jvm import util_rules as jvm_util_rules
from pants.jvm.dependency_inference import symbol_mapper
from pants.jvm.goals import lockfile
Expand Down Expand Up @@ -39,6 +39,7 @@ def rules():
*jdk_rules.rules(),
*jvm_tool.rules(),
*run.rules(),
*run_deploy_jar.rules(),
*war_rules(),
]

Expand Down
1 change: 1 addition & 0 deletions src/python/pants/jvm/package/deploy_jar.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class DeployJarFieldSet(PackageFieldSet, RunFieldSet):
JvmMainClassNameField,
JvmJdkField,
Dependencies,
OutputPathField,
)
run_in_sandbox_behavior = RunInSandboxBehavior.RUN_REQUEST_HERMETIC

Expand Down
Loading

0 comments on commit 672ca1d

Please sign in to comment.