worried-painter-31382
01/18/2023, 8:36 AMworried-painter-31382
01/18/2023, 8:37 AM# dependencies.py
from __future__ import annotations
import itertools
import os
from dataclasses import dataclass
from pathlib import Path
from typing import Iterable
from pants.backend.python.target_types import PythonSourceField
from pants.build_graph.address import Address
from pants.engine.addresses import Addresses
from pants.engine.fs import Digest
from pants.engine.fs import DigestContents
from pants.engine.internals.graph import Owners
from pants.engine.internals.graph import OwnersRequest
from pants.engine.internals.selectors import MultiGet
from pants.engine.rules import collect_rules
from pants.engine.rules import Get
from pants.engine.rules import rule
from pants.engine.rules import Rule
from pants.engine.rules import rule_helper
from pants.engine.target import FieldSet
from pants.engine.target import HydratedSources
from pants.engine.target import HydrateSourcesRequest
from pants.engine.target import InferDependenciesRequest
from pants.engine.target import InferredDependencies
from pants.engine.target import Targets
from pants.engine.unions import UnionRule
from .project_file import QtProjectFileDependenciesField
from .project_file import QtProjectSourceField
from .translation_file import QtTranslationSourceField
import logging
_logger = logging.getLogger(__name__)
@dataclass(frozen=True)
class QtProjectFileInferenceFieldSet(FieldSet):
required_fields = (QtProjectSourceField, QtProjectFileDependenciesField,)
class InferQtProjectFileDependencies(InferDependenciesRequest):
infer_from = QtProjectFileInferenceFieldSet
@dataclass(frozen=True)
class QtProjectContents:
path: str
sources: Iterable[Address] = ()
translations: Iterable[Address] = ()
@dataclass(frozen=True)
class ParseQtProjectFileRequest:
address: Address
@rule_helper
async def _parse_qt_project_file(request: ParseQtProjectFileRequest) -> QtProjectContents:
target = await Get(Targets, Addresses([request.address]))
source = await Get(
HydratedSources,
HydrateSourcesRequest(
target.expect_single()[QtProjectSourceField], for_sources_types=[QtProjectSourceField]
),
)
digest_contents = await Get(DigestContents, Digest, source.snapshot.digest)
file_content = digest_contents[0].content.decode()
continuation_lines_removed = file_content.replace('\\', '')
files = []
source_path = Path(source.snapshot.files[0])
for maybe_address in continuation_lines_removed.split():
if maybe_address.endswith('.py') or maybe_address.endswith('.ts'):
files.append(source_path.parent / Path(maybe_address))
owners = await MultiGet(Get(Owners, OwnersRequest((str(f),))) for f in files)
owner_tgts = await Get(Targets, Addresses(itertools.chain.from_iterable(owners)))
return QtProjectContents(
path=digest_contents[0].path,
sources=Addresses([tgt.address for tgt in owner_tgts if tgt.has_field(PythonSourceField)]),
translations=Addresses(
[tgt.address for tgt in owner_tgts if tgt.has_field(QtTranslationSourceField)]
),
)
@rule
async def infer_qt_project_dependencies(
request: InferQtProjectFileDependencies
) -> InferredDependencies:
project_contents = await _parse_qt_project_file(
ParseQtProjectFileRequest(request.field_set.address)
)
return InferredDependencies([*project_contents.sources, *project_contents.translations])
def dependency_rules() -> Iterable[Rule]:
return [
*collect_rules(),
UnionRule(InferDependenciesRequest, InferQtProjectFileDependencies),
]worried-painter-31382
01/18/2023, 8:37 AM# project_file.py
from __future__ import annotations
from pants.engine.target import COMMON_TARGET_FIELDS
from pants.engine.target import Dependencies
from pants.engine.target import SingleSourceField
from pants.engine.target import Target
class QtProjectFileDependenciesField(Dependencies):
supports_transitive_excludes = True
class QtProjectSourceField(SingleSourceField):
expected_file_extensions = ('.pro',)
uses_source_roots = True
class QtProjectFileTarget(Target):
alias = "qt_project_file"
core_fields = (QtProjectSourceField, QtProjectFileDependenciesField, *COMMON_TARGET_FIELDS)
help = "A qt project file."worried-painter-31382
01/18/2023, 8:39 AM# register.py
from __future__ import annotations
from typing import Iterable
from pants.engine.rules import Rule
from pants.engine.target import Target
from .dependencies import dependency_rules
from .project_file import QtProjectFileTarget
from .translation_file import QtTranslationFileTarget
def rules() -> Iterable[Rule]:
return [
*dependency_rules(),
]
def target_types() -> Iterable[type(Target)]:
return [QtProjectFileTarget, QtTranslationFileTarget]worried-painter-31382
01/18/2023, 8:39 AM# translation_file.py
from __future__ import annotations
from pants.engine.target import COMMON_TARGET_FIELDS
from pants.engine.target import SingleSourceField
from pants.engine.target import Target
class QtTranslationSourceField(SingleSourceField):
expected_file_extensions = ('.ts',)
uses_source_roots = True
class QtTranslationFileTarget(Target):
alias = "qt_translation_file"
core_fields = (QtTranslationSourceField, *COMMON_TARGET_FIELDS)
help = "A qt translation file."worried-painter-31382
01/18/2023, 8:40 AMworried-painter-31382
01/18/2023, 8:43 AM# BUILD
qt_project_file(name="qt_project", source="<http://file.pro|file.pro>")`worried-painter-31382
01/18/2023, 8:44 AMinfer_qt_project_dependencies when I do ./pants dependencies <http://file.pro|file.pro>worried-painter-31382
01/18/2023, 8:47 AMworried-painter-31382
01/18/2023, 10:58 AMworried-painter-31382
01/18/2023, 12:33 PMdef test_dependencies_goal(self, rule_runner: RuleRunner):
rule_runner.write_files(
{
"project/project.pro": "SOURCES += file.py",
"project/file.py": "print('hi')",
"project/BUILD": dedent(
"""\
python_sources()
qt_project_file(name="qt_project", source="<http://project.pro|project.pro>")
"""
),
}
)
result = rule_runner.run_goal_rule(Dependencies, args=["project:qt_project"])
assert result.stdout == "project/file.py\n"
passes
so the test setup is somehow different from real world in a not obvious way to me...worried-painter-31382
01/19/2023, 4:48 PM