From 12388c69a83ca3ee60c76f84a297afe0cea12739 Mon Sep 17 00:00:00 2001 From: Tom Dyas Date: Thu, 22 Jul 2021 11:41:10 -0400 Subject: [PATCH] infer go_external_module targets [ci skip-rust] [ci skip-build-wheels] --- src/python/pants/backend/go/tailor.py | 68 ++++++++++++++- src/python/pants/backend/go/tailor_test.py | 96 ++++++++++++++++++---- 2 files changed, 146 insertions(+), 18 deletions(-) diff --git a/src/python/pants/backend/go/tailor.py b/src/python/pants/backend/go/tailor.py index 8be4d5a4ba28..a098e40e3427 100644 --- a/src/python/pants/backend/go/tailor.py +++ b/src/python/pants/backend/go/tailor.py @@ -1,9 +1,12 @@ # Copyright 2021 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). +import itertools import os from dataclasses import dataclass -from pants.backend.go.target_types import GoModule, GoPackage +from pants.backend.go.module import ResolvedGoModule, ResolveGoModuleRequest +from pants.backend.go.target_types import GoExternalModule, GoModule, GoModuleSources, GoPackage +from pants.base.specs import AddressSpecs, MaybeEmptyDescendantAddresses, MaybeEmptySiblingAddresses from pants.core.goals.tailor import ( AllOwnedSources, PutativeTarget, @@ -14,6 +17,7 @@ from pants.engine.fs import PathGlobs, Paths from pants.engine.internals.selectors import Get from pants.engine.rules import collect_rules, rule +from pants.engine.target import UnexpandedTargets from pants.engine.unions import UnionRule from pants.util.logging import LogLevel @@ -70,9 +74,71 @@ async def find_putative_go_module_targets( return PutativeTargets(putative_targets) +def compute_go_external_module_target_name(name: str, version: str) -> str: + return f"{name.replace('/', '_')}_{version}" + + +@dataclass(frozen=True) +class PutativeGoExternalModuleTargetsRequest(PutativeTargetsRequest): + pass + + +@rule(level=LogLevel.DEBUG, desc="Determine candidate Go `go_external_module` targets to create") +async def find_putative_go_external_module_targets( + request: PutativeGoExternalModuleTargetsRequest, _all_owned_sources: AllOwnedSources +) -> PutativeTargets: + # Unlike ordinary tailor invocations, this rule looks at existing `go_module` targets and not at actual + # source files because it infers `go_external_module` targets based on go.mod contents. (This may require + # invoking `tailor` first to create `go_module` targets and then again to create `go_external_module` + # targets.) + + addresses = itertools.chain.from_iterable( + [ + [MaybeEmptySiblingAddresses(search_path), MaybeEmptyDescendantAddresses(search_path)] + for search_path in request.search_paths.dirs + ] + ) + candidate_targets = await Get(UnexpandedTargets, AddressSpecs(addresses)) + go_module_targets = [ + tgt + for tgt in candidate_targets + if tgt.has_field(GoModuleSources) and not tgt.address.is_file_target + ] + + putative_targets = [] + + for go_module_target in go_module_targets: + resolved_go_module = await Get( + ResolvedGoModule, ResolveGoModuleRequest(go_module_target.address) + ) + for module_descriptor in resolved_go_module.modules: + putative_targets.append( + PutativeTarget.for_target_type( + GoExternalModule, + go_module_target.address.spec_path, + compute_go_external_module_target_name( + module_descriptor.module_path, module_descriptor.module_version + ), + [], + kwargs={ + "path": module_descriptor.module_path, + "version": module_descriptor.module_version, + "import_path": module_descriptor.import_path, + }, + build_file_name="BUILD.godeps", + comments=( + "Auto-generated by `./pants tailor`. Re-run `./pants tailor` if go.mod changes.", + ), + ) + ) + + return PutativeTargets(putative_targets) + + def rules(): return [ *collect_rules(), UnionRule(PutativeTargetsRequest, PutativeGoPackageTargetsRequest), UnionRule(PutativeTargetsRequest, PutativeGoModuleTargetsRequest), + UnionRule(PutativeTargetsRequest, PutativeGoExternalModuleTargetsRequest), ] diff --git a/src/python/pants/backend/go/tailor_test.py b/src/python/pants/backend/go/tailor_test.py index 268d6ef3d695..1648b1c74dc3 100644 --- a/src/python/pants/backend/go/tailor_test.py +++ b/src/python/pants/backend/go/tailor_test.py @@ -1,8 +1,18 @@ # Copyright 2021 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). -from pants.backend.go.tailor import PutativeGoModuleTargetsRequest, PutativeGoPackageTargetsRequest +import textwrap + +import pytest + +from pants.backend.go import module, target_type_rules +from pants.backend.go.module import ResolvedGoModule, ResolveGoModuleRequest +from pants.backend.go.tailor import ( + PutativeGoExternalModuleTargetsRequest, + PutativeGoModuleTargetsRequest, + PutativeGoPackageTargetsRequest, +) from pants.backend.go.tailor import rules as go_tailor_rules -from pants.backend.go.target_types import GoModule, GoPackage +from pants.backend.go.target_types import GoExternalModule, GoModule, GoPackage from pants.core.goals.tailor import ( AllOwnedSources, PutativeTarget, @@ -10,24 +20,42 @@ PutativeTargetsSearchPaths, ) from pants.core.goals.tailor import rules as core_tailor_rules +from pants.core.util_rules import external_tool, source_files +from pants.engine.addresses import Addresses from pants.engine.rules import QueryRule +from pants.engine.target import DependenciesRequest, UnexpandedTargets from pants.testutil.rule_runner import RuleRunner -def test_find_putative_go_package_targets() -> None: +@pytest.fixture +def rule_runner() -> RuleRunner: rule_runner = RuleRunner( rules=[ *core_tailor_rules(), *go_tailor_rules(), + *external_tool.rules(), + *source_files.rules(), + *module.rules(), + *target_type_rules.rules(), QueryRule(PutativeTargets, [PutativeGoPackageTargetsRequest, AllOwnedSources]), + QueryRule(PutativeTargets, [PutativeGoModuleTargetsRequest, AllOwnedSources]), + QueryRule(PutativeTargets, [PutativeGoExternalModuleTargetsRequest, AllOwnedSources]), QueryRule(AllOwnedSources, ()), + QueryRule(Addresses, (DependenciesRequest,)), + QueryRule(UnexpandedTargets, (Addresses,)), + QueryRule(ResolvedGoModule, [ResolveGoModuleRequest]), ], target_types=[ GoPackage, + GoModule, + GoExternalModule, ], ) rule_runner.set_options(["--backend-packages=pants.backend.experimental.go"]) + return rule_runner + +def test_find_putative_go_package_targets(rule_runner: RuleRunner) -> None: rule_runner.write_files( { "src/go/owned/BUILD": "go_package()\n", @@ -60,20 +88,7 @@ def test_find_putative_go_package_targets() -> None: ) -def test_find_putative_go_module_targets() -> None: - rule_runner = RuleRunner( - rules=[ - *core_tailor_rules(), - *go_tailor_rules(), - QueryRule(PutativeTargets, [PutativeGoModuleTargetsRequest, AllOwnedSources]), - QueryRule(AllOwnedSources, ()), - ], - target_types=[ - GoModule, - ], - ) - rule_runner.set_options(["--backend-packages=pants.backend.experimental.go"]) - +def test_find_putative_go_module_targets(rule_runner: RuleRunner) -> None: rule_runner.write_files( { "src/go/owned/BUILD": "go_module()\n", @@ -104,3 +119,50 @@ def test_find_putative_go_module_targets() -> None: ), ] ) + + +def test_find_putative_go_external_module_targets(rule_runner: RuleRunner) -> None: + rule_runner.write_files( + { + "src/go/BUILD": "go_module()\n", + "src/go/go.mod": textwrap.dedent( + """\ + module example.com/src/go + go 1.16 + require ( + github.com/google/uuid v1.2.0 + ) + """ + ), + } + ) + putative_targets = rule_runner.request( + PutativeTargets, + [ + PutativeGoExternalModuleTargetsRequest(PutativeTargetsSearchPaths(("src/",))), + AllOwnedSources( + [ + "src/go/go.mod", + ] + ), + ], + ) + assert putative_targets == PutativeTargets( + [ + PutativeTarget.for_target_type( + GoExternalModule, + "src/go", + "github.com_google_uuid_v1.2.0", + [], + kwargs={ + "path": "github.com/google/uuid", + "version": "v1.2.0", + "import_path": "github.com/google/uuid", + }, + build_file_name="BUILD.godeps", + comments=( + "Auto-generated by `./pants tailor`. Re-run `./pants tailor` if go.mod changes.", + ), + ), + ] + )