From d319f4f9f8cfa6b8ab95e6820aaa77b70b6e3ce2 Mon Sep 17 00:00:00 2001 From: Eric Arellano Date: Thu, 21 Oct 2021 17:17:13 -0700 Subject: [PATCH] [internal] Hook up `check` to Go [ci skip-rust] [ci skip-build-wheels] --- .../pants/backend/experimental/go/register.py | 3 +- src/python/pants/backend/go/goals/check.py | 76 +++++++++++++++++ .../pants/backend/go/goals/check_test.py | 85 +++++++++++++++++++ 3 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 src/python/pants/backend/go/goals/check.py create mode 100644 src/python/pants/backend/go/goals/check_test.py diff --git a/src/python/pants/backend/experimental/go/register.py b/src/python/pants/backend/experimental/go/register.py index 93f994cf4c9..62b6072c550 100644 --- a/src/python/pants/backend/experimental/go/register.py +++ b/src/python/pants/backend/experimental/go/register.py @@ -2,7 +2,7 @@ # Licensed under the Apache License, Version 2.0 (see LICENSE). from pants.backend.go import target_type_rules -from pants.backend.go.goals import package_binary, run_binary, tailor, test +from pants.backend.go.goals import check, package_binary, run_binary, tailor, test from pants.backend.go.lint import fmt from pants.backend.go.lint.gofmt import skip_field as gofmt_skip_field from pants.backend.go.lint.gofmt.rules import rules as gofmt_rules @@ -34,6 +34,7 @@ def rules(): return [ *assembly.rules(), *build_pkg.rules(), + *check.rules(), *third_party_pkg.rules(), *golang.rules(), *import_analysis.rules(), diff --git a/src/python/pants/backend/go/goals/check.py b/src/python/pants/backend/go/goals/check.py new file mode 100644 index 00000000000..d70f30282b5 --- /dev/null +++ b/src/python/pants/backend/go/goals/check.py @@ -0,0 +1,76 @@ +# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from dataclasses import dataclass +from typing import cast + +from pants.backend.go.target_types import GoFirstPartyPackageSourcesField +from pants.backend.go.util_rules.build_pkg import ( + BuildGoPackageRequest, + BuildGoPackageTargetRequest, + FallibleBuildGoPackageRequest, + FallibleBuiltGoPackage, +) +from pants.core.goals.check import CheckRequest, CheckResult, CheckResults +from pants.engine.rules import Get, MultiGet, collect_rules, rule +from pants.engine.target import FieldSet +from pants.engine.unions import UnionRule + + +@dataclass(frozen=True) +class GoCheckFieldSet(FieldSet): + required_fields = (GoFirstPartyPackageSourcesField,) + + sources: GoFirstPartyPackageSourcesField + + +class GoCheckRequest(CheckRequest): + field_set_type = GoCheckFieldSet + + +@rule +async def check_go(request: GoCheckRequest) -> CheckResults: + build_requests = await MultiGet( + Get(FallibleBuildGoPackageRequest, BuildGoPackageTargetRequest(field_set.address)) + for field_set in request.field_sets + ) + invalid_requests = [] + valid_requests = [] + for fallible_request in build_requests: + if fallible_request.request is None: + invalid_requests.append(fallible_request) + else: + valid_requests.append(fallible_request.request) + + build_results = await MultiGet( + Get(FallibleBuiltGoPackage, BuildGoPackageRequest, request) for request in valid_requests + ) + + # TODO: Update `build_pkg.py` to use streaming workunits to log compilation results, which has + # the benefit of other contexts like `test.py` using it. Switch this to only preserve the + # exit code. + check_results = [ + *( + CheckResult( + result.exit_code, + "", + cast(str, result.stderr), + partition_description=result.import_path, + ) + for result in invalid_requests + ), + *( + CheckResult( + result.exit_code, + result.stdout or "", + result.stderr or "", + partition_description=result.import_path, + ) + for result in build_results + ), + ] + return CheckResults(check_results, checker_name="go") + + +def rules(): + return [*collect_rules(), UnionRule(CheckRequest, GoCheckRequest)] diff --git a/src/python/pants/backend/go/goals/check_test.py b/src/python/pants/backend/go/goals/check_test.py new file mode 100644 index 00000000000..672b1927158 --- /dev/null +++ b/src/python/pants/backend/go/goals/check_test.py @@ -0,0 +1,85 @@ +# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from __future__ import annotations + +from textwrap import dedent + +import pytest + +from pants.backend.go import target_type_rules +from pants.backend.go.goals import check +from pants.backend.go.goals.check import GoCheckFieldSet, GoCheckRequest +from pants.backend.go.target_types import GoModTarget +from pants.backend.go.util_rules import ( + assembly, + build_pkg, + first_party_pkg, + go_mod, + import_analysis, + sdk, + third_party_pkg, +) +from pants.core.goals.check import CheckResult, CheckResults +from pants.engine.addresses import Address +from pants.testutil.rule_runner import QueryRule, RuleRunner + + +@pytest.fixture +def rule_runner() -> RuleRunner: + rule_runner = RuleRunner( + rules=[ + *check.rules(), + *sdk.rules(), + *assembly.rules(), + *build_pkg.rules(), + *import_analysis.rules(), + *go_mod.rules(), + *first_party_pkg.rules(), + *third_party_pkg.rules(), + *target_type_rules.rules(), + QueryRule(CheckResults, [GoCheckRequest]), + ], + target_types=[GoModTarget], + ) + rule_runner.set_options([], env_inherit={"PATH"}) + return rule_runner + + +def test_check(rule_runner: RuleRunner) -> None: + rule_runner.write_files( + { + "go.mod": dedent( + """\ + module example.com/greeter + go 1.17 + """ + ), + "bad/f.go": "invalid!!!", + "good/f.go": dedent( + """\ + package greeter + + import "fmt" + + func Hello() { + fmt.Println("Hello world!") + } + """ + ), + "BUILD": "go_mod(name='mod')", + } + ) + targets = [ + rule_runner.get_target(Address("", target_name="mod", generated_name="./bad")), + rule_runner.get_target(Address("", target_name="mod", generated_name="./good")), + ] + results = rule_runner.request( + CheckResults, [GoCheckRequest(GoCheckFieldSet.create(tgt) for tgt in targets)] + ).results + assert set(results) == { + CheckResult(0, "", "", "example.com/greeter/good"), + CheckResult( + 1, "", "bad/f.go:1:1: expected 'package', found invalid\n", "example.com/greeter/bad" + ), + }