-
-
Notifications
You must be signed in to change notification settings - Fork 636
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow for codegen targets to be used directly by JVM compiler requests #14751
Changes from 8 commits
ee95331
9e866db
7a654d1
513f000
b0e78f9
f937aae
079c775
5580fc4
a228661
39890bd
c77afd1
66aef0a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# Copyright 2022 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
"""Generate Java sources from Protocol Buffers (Protobufs). | ||
|
||
See https://www.pantsbuild.org/docs/protobuf. | ||
""" | ||
|
||
from pants.backend.codegen import export_codegen_goal | ||
from pants.backend.codegen.protobuf import protobuf_dependency_inference | ||
from pants.backend.codegen.protobuf import tailor as protobuf_tailor | ||
from pants.backend.codegen.protobuf.java.rules import rules as java_rules | ||
from pants.backend.codegen.protobuf.target_types import ( | ||
ProtobufSourcesGeneratorTarget, | ||
ProtobufSourceTarget, | ||
) | ||
from pants.backend.codegen.protobuf.target_types import rules as protobuf_target_rules | ||
from pants.core.util_rules import stripped_source_files | ||
|
||
|
||
def rules(): | ||
return [ | ||
*java_rules(), | ||
*protobuf_dependency_inference.rules(), | ||
*protobuf_tailor.rules(), | ||
*export_codegen_goal.rules(), | ||
*protobuf_target_rules(), | ||
*stripped_source_files.rules(), | ||
] | ||
|
||
|
||
def target_types(): | ||
return [ProtobufSourcesGeneratorTarget, ProtobufSourceTarget] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# Copyright 2022 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
"""Generate Scala sources from Protocol Buffers (Protobufs). | ||
|
||
See https://www.pantsbuild.org/docs/protobuf. | ||
""" | ||
|
||
from pants.backend.codegen import export_codegen_goal | ||
from pants.backend.codegen.protobuf import protobuf_dependency_inference | ||
from pants.backend.codegen.protobuf import tailor as protobuf_tailor | ||
from pants.backend.codegen.protobuf.scala.rules import rules as scala_rules | ||
from pants.backend.codegen.protobuf.target_types import ( | ||
ProtobufSourcesGeneratorTarget, | ||
ProtobufSourceTarget, | ||
) | ||
from pants.backend.codegen.protobuf.target_types import rules as protobuf_target_rules | ||
from pants.core.util_rules import stripped_source_files | ||
|
||
|
||
def rules(): | ||
return [ | ||
*scala_rules(), | ||
*protobuf_dependency_inference.rules(), | ||
*protobuf_tailor.rules(), | ||
*export_codegen_goal.rules(), | ||
*protobuf_target_rules(), | ||
*stripped_source_files.rules(), | ||
] | ||
|
||
|
||
def target_types(): | ||
return [ProtobufSourcesGeneratorTarget, ProtobufSourceTarget] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,7 @@ | |
import logging | ||
import os | ||
from abc import ABCMeta | ||
from collections import deque | ||
from collections import defaultdict, deque | ||
from dataclasses import dataclass | ||
from enum import Enum, auto | ||
from typing import ClassVar, Iterable, Iterator, Sequence | ||
|
@@ -17,9 +17,16 @@ | |
from pants.engine.internals.selectors import Get, MultiGet | ||
from pants.engine.process import FallibleProcessResult | ||
from pants.engine.rules import collect_rules, rule | ||
from pants.engine.target import CoarsenedTarget, FieldSet | ||
from pants.engine.target import ( | ||
CoarsenedTarget, | ||
Field, | ||
FieldSet, | ||
GenerateSourcesRequest, | ||
SourcesField, | ||
) | ||
from pants.engine.unions import UnionMembership, union | ||
from pants.jvm.resolve.key import CoursierResolveKey | ||
from pants.util.frozendict import FrozenDict | ||
from pants.util.logging import LogLevel | ||
from pants.util.meta import frozen_after_init | ||
from pants.util.ordered_set import FrozenOrderedSet | ||
|
@@ -47,6 +54,38 @@ class _ClasspathEntryRequestClassification(Enum): | |
INCOMPATIBLE = auto() | ||
|
||
|
||
@dataclass(frozen=True) | ||
class JVMRequestTypes: | ||
classpath_entry_requests: tuple[type[ClasspathEntryRequest], ...] | ||
code_generator_requests: FrozenDict[type[SourcesField], tuple[type[ClasspathEntryRequest], ...]] | ||
|
||
|
||
@rule | ||
def calculate_jvm_request_types(union_membership: UnionMembership) -> JVMRequestTypes: | ||
cpe_impls = union_membership.get(ClasspathEntryRequest) | ||
impls_by_source: dict[type[Field], type[ClasspathEntryRequest]] = {} | ||
for impl in cpe_impls: | ||
for field_set in impl.field_sets: | ||
for field in field_set.required_fields: | ||
# Assume only one impl per field (normally sound) | ||
# (note that subsequently, we only check for `SourceFields`, so no need to filter) | ||
impls_by_source[field] = impl | ||
|
||
generators: Iterable[type[GenerateSourcesRequest]] = union_membership.get( | ||
GenerateSourcesRequest | ||
) | ||
|
||
usable_generators_: dict[type[SourcesField], list[type[ClasspathEntryRequest]]] = defaultdict( | ||
list | ||
) | ||
for g in generators: | ||
if g.output in impls_by_source: | ||
usable_generators_[g.input].append(impls_by_source[g.output]) | ||
usable_generators = FrozenDict((key, tuple(value)) for key, value in usable_generators_.items()) | ||
|
||
return JVMRequestTypes(tuple(cpe_impls), usable_generators) | ||
|
||
|
||
@union | ||
@dataclass(frozen=True) | ||
class ClasspathEntryRequest(metaclass=ABCMeta): | ||
|
@@ -77,7 +116,7 @@ class ClasspathEntryRequest(metaclass=ABCMeta): | |
|
||
@staticmethod | ||
def for_targets( | ||
union_membership: UnionMembership, | ||
jvm_request_types: JVMRequestTypes, | ||
component: CoarsenedTarget, | ||
resolve: CoursierResolveKey, | ||
*, | ||
|
@@ -89,10 +128,21 @@ def for_targets( | |
request types which are marked `root_only`. | ||
""" | ||
|
||
for (input, request_types) in jvm_request_types.code_generator_requests.items(): | ||
if not component.representative.has_field(input): | ||
continue | ||
if len(request_types) > 1: | ||
raise ClasspathSourceAmbiguity( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. at the moment, this will result in a late failure, and only if a user attempts to use a source file with conflicting implementations as a dependency. I could re-write to fail fast, but the code will eventually need to fail here once #14041 is in place. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's still not clear to me why this matching isn't happening inside of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup, that all checks out. I'll make that refactor before landing this. |
||
f"More than one code generator ({request_types}) was compatible with the " | ||
f"inputs:\n{component.bullet_list()}" | ||
) | ||
request_type = request_types[0] | ||
return request_type(component, resolve, None) | ||
|
||
compatible = [] | ||
partial = [] | ||
consume_only = [] | ||
impls = union_membership.get(ClasspathEntryRequest) | ||
impls = jvm_request_types.classpath_entry_requests | ||
for impl in impls: | ||
classification = ClasspathEntryRequest.classify_impl(impl, component) | ||
if classification == _ClasspathEntryRequestClassification.INCOMPATIBLE: | ||
|
@@ -341,7 +391,7 @@ def required_classfiles(fallible_result: FallibleClasspathEntry) -> ClasspathEnt | |
|
||
@rule | ||
def classpath_dependency_requests( | ||
union_membership: UnionMembership, request: ClasspathDependenciesRequest | ||
jvm_request_types: JVMRequestTypes, request: ClasspathDependenciesRequest | ||
) -> ClasspathEntryRequests: | ||
def ignore_because_generated(coarsened_dep: CoarsenedTarget) -> bool: | ||
if len(coarsened_dep.members) == 1: | ||
|
@@ -352,7 +402,7 @@ def ignore_because_generated(coarsened_dep: CoarsenedTarget) -> bool: | |
|
||
return ClasspathEntryRequests( | ||
ClasspathEntryRequest.for_targets( | ||
union_membership, component=coarsened_dep, resolve=request.request.resolve | ||
jvm_request_types, component=coarsened_dep, resolve=request.request.resolve | ||
) | ||
for coarsened_dep in request.request.component.dependencies | ||
if not request.ignore_generated or not ignore_because_generated(coarsened_dep) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: a more meaningful name would be good.