diff --git a/src/python/pants/backend/experimental/go/register.py b/src/python/pants/backend/experimental/go/register.py index fb06b84288b..9cb9fb18170 100644 --- a/src/python/pants/backend/experimental/go/register.py +++ b/src/python/pants/backend/experimental/go/register.py @@ -10,8 +10,8 @@ from pants.backend.go.target_types import ( GoBinaryTarget, GoExternalPackageTarget, + GoInternalPackageTarget, GoModTarget, - GoPackage, ) from pants.backend.go.util_rules import ( assembly, @@ -28,7 +28,7 @@ def target_types(): - return [GoModTarget, GoPackage, GoExternalPackageTarget, GoBinaryTarget] + return [GoInternalPackageTarget, GoModTarget, GoExternalPackageTarget, GoBinaryTarget] def rules(): diff --git a/src/python/pants/backend/go/goals/package_binary_integration_test.py b/src/python/pants/backend/go/goals/package_binary_integration_test.py index 6a081255da7..d721c59c0ca 100644 --- a/src/python/pants/backend/go/goals/package_binary_integration_test.py +++ b/src/python/pants/backend/go/goals/package_binary_integration_test.py @@ -12,7 +12,7 @@ from pants.backend.go import target_type_rules from pants.backend.go.goals import package_binary from pants.backend.go.goals.package_binary import GoBinaryFieldSet -from pants.backend.go.target_types import GoBinaryTarget, GoModTarget, GoPackage +from pants.backend.go.target_types import GoBinaryTarget, GoModTarget from pants.backend.go.util_rules import ( assembly, build_go_pkg, @@ -50,7 +50,7 @@ def rule_runner() -> RuleRunner: *sdk.rules(), QueryRule(BuiltPackage, (GoBinaryFieldSet,)), ], - target_types=[GoBinaryTarget, GoPackage, GoModTarget], + target_types=[GoBinaryTarget, GoModTarget], ) rule_runner.set_options([], env_inherit={"PATH"}) return rule_runner @@ -88,7 +88,6 @@ def test_package_simple(rule_runner: RuleRunner) -> None: "BUILD": dedent( """\ go_mod(name='mod') - go_package(name='pkg') go_binary(name='bin') """ ), @@ -125,7 +124,6 @@ def test_package_with_dependencies(rule_runner: RuleRunner) -> None: } """ ), - "lib/BUILD": "go_package()", "main.go": dedent( """\ package main @@ -165,8 +163,8 @@ def test_package_with_dependencies(rule_runner: RuleRunner) -> None: "BUILD": dedent( """\ go_mod(name='mod') - go_package(name='pkg') - go_binary(name='bin') + # TODO: Remove `main` once it's fixed for target gen. + go_binary(name='bin', main='//:mod#./') """ ), } diff --git a/src/python/pants/backend/go/goals/tailor.py b/src/python/pants/backend/go/goals/tailor.py index 52d2f4388ab..b2f9ca354b2 100644 --- a/src/python/pants/backend/go/goals/tailor.py +++ b/src/python/pants/backend/go/goals/tailor.py @@ -4,7 +4,7 @@ import os from dataclasses import dataclass -from pants.backend.go.target_types import GoModTarget, GoPackage +from pants.backend.go.target_types import GoModTarget from pants.core.goals.tailor import ( AllOwnedSources, PutativeTarget, @@ -19,32 +19,6 @@ from pants.util.logging import LogLevel -@dataclass(frozen=True) -class PutativeGoPackageTargetsRequest(PutativeTargetsRequest): - pass - - -@rule(level=LogLevel.DEBUG, desc="Determine candidate Go `go_package` targets to create") -async def find_putative_go_package_targets( - request: PutativeGoPackageTargetsRequest, all_owned_sources: AllOwnedSources -) -> PutativeTargets: - all_go_files = await Get(Paths, PathGlobs, request.search_paths.path_globs("*.go")) - unowned_go_files = set(all_go_files.files) - set(all_owned_sources) - - putative_targets = [] - for dirname, filenames in group_by_dir(unowned_go_files).items(): - putative_targets.append( - PutativeTarget.for_target_type( - GoPackage, - dirname, - os.path.basename(dirname), - sorted(filenames), - ) - ) - - return PutativeTargets(putative_targets) - - @dataclass(frozen=True) class PutativeGoModuleTargetsRequest(PutativeTargetsRequest): pass @@ -62,9 +36,9 @@ async def find_putative_go_mod_targets( putative_targets.append( PutativeTarget.for_target_type( GoModTarget, - dirname, - os.path.basename(dirname), - sorted(filenames), + path=dirname, + name=os.path.basename(dirname), + triggering_sources=sorted(filenames), ) ) @@ -72,8 +46,4 @@ async def find_putative_go_mod_targets( def rules(): - return [ - *collect_rules(), - UnionRule(PutativeTargetsRequest, PutativeGoPackageTargetsRequest), - UnionRule(PutativeTargetsRequest, PutativeGoModuleTargetsRequest), - ] + return [*collect_rules(), UnionRule(PutativeTargetsRequest, PutativeGoModuleTargetsRequest)] diff --git a/src/python/pants/backend/go/goals/tailor_test.py b/src/python/pants/backend/go/goals/tailor_test.py index 07c717eb8db..5a41d06a9a4 100644 --- a/src/python/pants/backend/go/goals/tailor_test.py +++ b/src/python/pants/backend/go/goals/tailor_test.py @@ -3,63 +3,27 @@ import pytest -from pants.backend.go import target_type_rules -from pants.backend.go.goals.tailor import ( - PutativeGoModuleTargetsRequest, - PutativeGoPackageTargetsRequest, -) +from pants.backend.go.goals.tailor import PutativeGoModuleTargetsRequest from pants.backend.go.goals.tailor import rules as go_tailor_rules -from pants.backend.go.target_types import GoModTarget, GoPackage -from pants.backend.go.util_rules import external_pkg, go_mod, sdk +from pants.backend.go.target_types import GoModTarget from pants.core.goals.tailor import ( AllOwnedSources, PutativeTarget, PutativeTargets, 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.rules import QueryRule from pants.testutil.rule_runner import RuleRunner @pytest.fixture def rule_runner() -> RuleRunner: - rule_runner = RuleRunner( + return RuleRunner( rules=[ - *core_tailor_rules(), *go_tailor_rules(), - *external_tool.rules(), - *source_files.rules(), - *external_pkg.rules(), - *go_mod.rules(), - *sdk.rules(), - *target_type_rules.rules(), - QueryRule(PutativeTargets, [PutativeGoPackageTargetsRequest, AllOwnedSources]), QueryRule(PutativeTargets, [PutativeGoModuleTargetsRequest, AllOwnedSources]), ], - target_types=[GoPackage, GoModTarget], - ) - 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", - "src/go/owned/src.go": "package owned\n", - "src/go/unowned/src.go": "package unowned\n", - } - ) - putative_targets = rule_runner.request( - PutativeTargets, - [ - PutativeGoPackageTargetsRequest(PutativeTargetsSearchPaths(("src/",))), - AllOwnedSources(["src/go/owned/src.go"]), - ], - ) - assert putative_targets == PutativeTargets( - [PutativeTarget.for_target_type(GoPackage, "src/go/unowned", "unowned", ["src.go"])] + target_types=[GoModTarget], ) @@ -79,5 +43,9 @@ def test_find_putative_go_mod_targets(rule_runner: RuleRunner) -> None: ], ) assert putative_targets == PutativeTargets( - [PutativeTarget.for_target_type(GoModTarget, "src/go/unowned", "unowned", ["go.mod"])] + [ + PutativeTarget.for_target_type( + GoModTarget, path="src/go/unowned", name="unowned", triggering_sources=["go.mod"] + ) + ] ) diff --git a/src/python/pants/backend/go/goals/test.py b/src/python/pants/backend/go/goals/test.py index 7afdd5dc17d..dc751b65b5f 100644 --- a/src/python/pants/backend/go/goals/test.py +++ b/src/python/pants/backend/go/goals/test.py @@ -1,14 +1,18 @@ # Copyright 2021 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). -from pants.backend.go.target_types import GoPackageSources + +from __future__ import annotations + +from pants.backend.go.target_types import GoInternalPackageSourcesField from pants.core.goals.test import TestDebugRequest, TestFieldSet, TestResult from pants.engine.rules import collect_rules, rule from pants.engine.unions import UnionRule class GoTestFieldSet(TestFieldSet): - required_fields = (GoPackageSources,) - sources: GoPackageSources + required_fields = (GoInternalPackageSourcesField,) + + sources: GoInternalPackageSourcesField @rule diff --git a/src/python/pants/backend/go/goals/test_test.py b/src/python/pants/backend/go/goals/test_test.py index 4f6afe9b62f..5e60cfb215a 100644 --- a/src/python/pants/backend/go/goals/test_test.py +++ b/src/python/pants/backend/go/goals/test_test.py @@ -1,15 +1,18 @@ # Copyright 2021 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). + +from __future__ import annotations + import pytest +from pants.backend.go import target_type_rules from pants.backend.go.goals.test import GoTestFieldSet from pants.backend.go.goals.test import rules as test_rules -from pants.backend.go.target_types import GoModTarget, GoPackage +from pants.backend.go.target_types import GoModTarget +from pants.backend.go.util_rules import external_pkg, go_mod, go_pkg, sdk from pants.build_graph.address import Address from pants.core.goals.test import TestResult -from pants.engine.internals.scheduler import ExecutionError -from pants.engine.rules import QueryRule -from pants.testutil.rule_runner import RuleRunner +from pants.testutil.rule_runner import QueryRule, RuleRunner, engine_error @pytest.fixture @@ -17,25 +20,27 @@ def rule_runner() -> RuleRunner: rule_runner = RuleRunner( rules=[ *test_rules(), + *go_mod.rules(), + *go_pkg.rules(), + *external_pkg.rules(), + *sdk.rules(), + *target_type_rules.rules(), QueryRule(TestResult, [GoTestFieldSet]), ], - target_types=[GoPackage, GoModTarget], + target_types=[GoModTarget], ) + rule_runner.set_options([], env_inherit={"PATH"}) return rule_runner def test_stub_is_a_stub(rule_runner: RuleRunner) -> None: rule_runner.write_files( { - "foo/BUILD": "go_mod()\ngo_package(name='lib')\n", - "foo/go.mod": "module foo\n", - "foo/go.sum": "", - "foo/bar_test.go": "package foo\n", + "foo/BUILD": "go_mod()", + "foo/go.mod": "module foo", + "foo/bar_test.go": "package foo", } ) - - with pytest.raises(ExecutionError) as exc_info: - tgt = rule_runner.get_target(Address("foo", target_name="lib")) + tgt = rule_runner.get_target(Address("foo", generated_name="./")) + with engine_error(NotImplementedError): rule_runner.request(TestResult, [GoTestFieldSet.create(tgt)]) - - assert "NotImplementedError: This is a stub." in str(exc_info.value) diff --git a/src/python/pants/backend/go/lint/fmt.py b/src/python/pants/backend/go/lint/fmt.py index 71bdd263f3b..6d498f0ff2f 100644 --- a/src/python/pants/backend/go/lint/fmt.py +++ b/src/python/pants/backend/go/lint/fmt.py @@ -4,7 +4,7 @@ from dataclasses import dataclass from typing import Iterable -from pants.backend.go.target_types import GoSources +from pants.backend.go.target_types import GoInternalPackageSourcesField from pants.core.goals.fmt import FmtResult, LanguageFmtResults, LanguageFmtTargets from pants.core.goals.style_request import StyleRequest from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest @@ -18,7 +18,7 @@ # While gofmt is one of those, this is more generic and can work with other formatters. @dataclass(frozen=True) class GoLangFmtTargets(LanguageFmtTargets): - required_fields = (GoSources,) + required_fields = (GoInternalPackageSourcesField,) @union @@ -32,7 +32,9 @@ async def format_golang_targets( ) -> LanguageFmtResults: original_sources = await Get( SourceFiles, - SourceFilesRequest(target[GoSources] for target in go_fmt_targets.targets), + SourceFilesRequest( + target[GoInternalPackageSourcesField] for target in go_fmt_targets.targets + ), ) prior_formatter_result = original_sources.snapshot diff --git a/src/python/pants/backend/go/lint/gofmt/rules.py b/src/python/pants/backend/go/lint/gofmt/rules.py index 898aa83f153..5106e44245f 100644 --- a/src/python/pants/backend/go/lint/gofmt/rules.py +++ b/src/python/pants/backend/go/lint/gofmt/rules.py @@ -12,7 +12,7 @@ from pants.backend.go.lint.gofmt.subsystem import GofmtSubsystem from pants.backend.go.subsystems import golang from pants.backend.go.subsystems.golang import GoRoot -from pants.backend.go.target_types import GoSources +from pants.backend.go.target_types import GoInternalPackageSourcesField from pants.core.goals.fmt import FmtResult from pants.core.goals.lint import LintRequest, LintResult, LintResults from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest @@ -28,9 +28,9 @@ @dataclass(frozen=True) class GofmtFieldSet(FieldSet): - required_fields = (GoSources,) + required_fields = (GoInternalPackageSourcesField,) - sources: GoSources + sources: GoInternalPackageSourcesField @classmethod def opt_out(cls, tgt: Target) -> bool: diff --git a/src/python/pants/backend/go/lint/gofmt/rules_integration_test.py b/src/python/pants/backend/go/lint/gofmt/rules_integration_test.py index fe464cf3404..4f28865c92b 100644 --- a/src/python/pants/backend/go/lint/gofmt/rules_integration_test.py +++ b/src/python/pants/backend/go/lint/gofmt/rules_integration_test.py @@ -10,7 +10,7 @@ from pants.backend.go.lint import fmt from pants.backend.go.lint.gofmt.rules import GofmtFieldSet, GofmtRequest from pants.backend.go.lint.gofmt.rules import rules as gofmt_rules -from pants.backend.go.target_types import GoBinaryTarget, GoPackage +from pants.backend.go.target_types import GoInternalPackageTarget from pants.core.goals.fmt import FmtResult from pants.core.goals.lint import LintResult, LintResults from pants.core.util_rules import source_files @@ -24,7 +24,7 @@ @pytest.fixture() def rule_runner() -> RuleRunner: return RuleRunner( - target_types=[GoBinaryTarget, GoPackage], + target_types=[GoInternalPackageTarget], rules=[ *fmt.rules(), *gofmt_rules(), @@ -78,6 +78,9 @@ def rule_runner() -> RuleRunner: ) +PKG_TGT = "_go_internal_package(name='t', import_path='doesnt_matter', subpath='')" + + def run_gofmt( rule_runner: RuleRunner, targets: list[Target], @@ -108,7 +111,7 @@ def get_digest(rule_runner: RuleRunner, source_files: dict[str, str]) -> Digest: def test_passing(rule_runner: RuleRunner) -> None: - rule_runner.write_files({"f.go": GOOD_FILE, "BUILD": "go_package(name='t')"}) + rule_runner.write_files({"f.go": GOOD_FILE, "BUILD": PKG_TGT}) tgt = rule_runner.get_target(Address("", target_name="t")) lint_results, fmt_result = run_gofmt(rule_runner, [tgt]) assert len(lint_results) == 1 @@ -120,7 +123,7 @@ def test_passing(rule_runner: RuleRunner) -> None: def test_failing(rule_runner: RuleRunner) -> None: - rule_runner.write_files({"f.go": BAD_FILE, "BUILD": "go_package(name='t')"}) + rule_runner.write_files({"f.go": BAD_FILE, "BUILD": PKG_TGT}) tgt = rule_runner.get_target(Address("", target_name="t")) lint_results, fmt_result = run_gofmt(rule_runner, [tgt]) assert len(lint_results) == 1 @@ -132,9 +135,7 @@ def test_failing(rule_runner: RuleRunner) -> None: def test_mixed_sources(rule_runner: RuleRunner) -> None: - rule_runner.write_files( - {"good.go": GOOD_FILE, "bad.go": BAD_FILE, "BUILD": "go_package(name='t')"} - ) + rule_runner.write_files({"good.go": GOOD_FILE, "bad.go": BAD_FILE, "BUILD": PKG_TGT}) tgt = rule_runner.get_target(Address("", target_name="t")) lint_results, fmt_result = run_gofmt(rule_runner, [tgt]) assert len(lint_results) == 1 @@ -151,14 +152,14 @@ def test_multiple_targets(rule_runner: RuleRunner) -> None: rule_runner.write_files( { "good/f.go": GOOD_FILE, - "good/BUILD": "go_package()", + "good/BUILD": PKG_TGT, "bad/f.go": BAD_FILE, - "bad/BUILD": "go_package()", + "bad/BUILD": PKG_TGT, } ) tgts = [ - rule_runner.get_target(Address("good")), - rule_runner.get_target(Address("bad")), + rule_runner.get_target(Address("good", target_name="t")), + rule_runner.get_target(Address("bad", target_name="t")), ] lint_results, fmt_result = run_gofmt(rule_runner, tgts) assert len(lint_results) == 1 @@ -172,7 +173,7 @@ def test_multiple_targets(rule_runner: RuleRunner) -> None: def test_skip(rule_runner: RuleRunner) -> None: - rule_runner.write_files({"f.go": BAD_FILE, "BUILD": "go_package(name='t')"}) + rule_runner.write_files({"f.go": BAD_FILE, "BUILD": PKG_TGT}) tgt = rule_runner.get_target(Address("", target_name="t")) lint_results, fmt_result = run_gofmt(rule_runner, [tgt], extra_args=["--gofmt-skip"]) assert not lint_results diff --git a/src/python/pants/backend/go/lint/gofmt/skip_field.py b/src/python/pants/backend/go/lint/gofmt/skip_field.py index 00c5fee4019..15525b8c4be 100644 --- a/src/python/pants/backend/go/lint/gofmt/skip_field.py +++ b/src/python/pants/backend/go/lint/gofmt/skip_field.py @@ -1,7 +1,7 @@ # Copyright 2021 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). -from pants.backend.go.target_types import GoPackage +from pants.backend.go.target_types import GoInternalPackageTarget from pants.engine.target import BoolField @@ -12,4 +12,4 @@ class SkipGofmtField(BoolField): def rules(): - return [GoPackage.register_plugin_field(SkipGofmtField)] + return [GoInternalPackageTarget.register_plugin_field(SkipGofmtField)] diff --git a/src/python/pants/backend/go/target_type_rules.py b/src/python/pants/backend/go/target_type_rules.py index a91dca47025..2795fe610c7 100644 --- a/src/python/pants/backend/go/target_type_rules.py +++ b/src/python/pants/backend/go/target_type_rules.py @@ -14,13 +14,14 @@ GoBinaryMainPackageRequest, GoExternalModulePathField, GoExternalModuleVersionField, - GoExternalPackageDependencies, - GoExternalPackageImportPathField, + GoExternalPackageDependenciesField, GoExternalPackageTarget, - GoImportPath, + GoImportPathField, + GoInternalPackageDependenciesField, + GoInternalPackageSourcesField, + GoInternalPackageSubpathField, + GoInternalPackageTarget, GoModTarget, - GoPackageDependencies, - GoPackageSources, ) from pants.backend.go.util_rules import go_pkg, import_analysis from pants.backend.go.util_rules.external_pkg import ( @@ -38,14 +39,10 @@ from pants.backend.go.util_rules.go_pkg import ResolvedGoPackage, ResolveGoPackageRequest from pants.backend.go.util_rules.import_analysis import GoStdLibImports from pants.base.exceptions import ResolveError -from pants.base.specs import ( - AddressSpecs, - DescendantAddresses, - MaybeEmptyDescendantAddresses, - MaybeEmptySiblingAddresses, - SiblingAddresses, -) +from pants.base.specs import AddressSpecs, DescendantAddresses, SiblingAddresses +from pants.core.goals.tailor import group_by_dir from pants.engine.addresses import Address, AddressInput +from pants.engine.fs import PathGlobs, Paths from pants.engine.rules import Get, MultiGet, collect_rules, rule from pants.engine.target import ( GeneratedTargets, @@ -65,9 +62,9 @@ logger = logging.getLogger(__name__) -# Inject a dependency between a go_package and its owning go_mod. +# Inject a dependency between an _internal_go_package and its owning go_mod. class InjectGoPackageDependenciesRequest(InjectDependenciesRequest): - inject_for = GoPackageDependencies + inject_for = GoInternalPackageDependenciesField @rule @@ -80,120 +77,70 @@ async def inject_go_package_dependencies( # TODO: Figure out how to merge (or not) this with ResolvedImportPaths as a base class. @dataclass(frozen=True) -class GoImportPathToPackageMapping: +class ImportPathToPackages: # Maps import paths to the address of go_package or (more likely) go_external_package targets. mapping: FrozenDict[str, tuple[Address, ...]] @rule -async def analyze_import_path_to_package_mapping() -> GoImportPathToPackageMapping: +async def map_import_paths_to_packages() -> ImportPathToPackages: mapping: dict[str, list[Address]] = defaultdict(list) - all_targets = await Get(Targets, AddressSpecs([DescendantAddresses("")])) for tgt in all_targets: - if not tgt.has_field(GoImportPath): - continue - - # Note: This will usually skip go_package targets since they need analysis to infer the import path - # since there is no way in the engine to attach inferred values as fields. - import_path = tgt[GoImportPath].value - if not import_path: + if not tgt.has_field(GoImportPathField): continue - + import_path = tgt[GoImportPathField].value mapping[import_path].append(tgt.address) - frozen_mapping = FrozenDict({ip: tuple(tgts) for ip, tgts in mapping.items()}) - return GoImportPathToPackageMapping(mapping=frozen_mapping) + return ImportPathToPackages(frozen_mapping) +# TODO: Use dependency injection. This doesn't actually look at the Sources field. class InferGoPackageDependenciesRequest(InferDependenciesRequest): - infer_from = GoPackageSources + infer_from = GoInternalPackageSourcesField -# TODO(12761): Refactor this rule so as much as possible is memoized by invoking other rules. Consider -# for example `FirstPartyPythonModuleMapping` and `ThirdPartyPythonModuleMapping`. @rule async def infer_go_dependencies( request: InferGoPackageDependenciesRequest, std_lib_imports: GoStdLibImports, - package_mapping: GoImportPathToPackageMapping, + package_mapping: ImportPathToPackages, ) -> InferredDependencies: this_go_package = await Get( ResolvedGoPackage, ResolveGoPackageRequest(request.sources_field.address) ) - # Obtain all go_package targets under this package's go_mod. - assert this_go_package.module_address is not None - spec_path = this_go_package.module_address.spec_path - address_specs = [ - MaybeEmptySiblingAddresses(spec_path), - MaybeEmptyDescendantAddresses(spec_path), - ] - candidate_targets = await Get(Targets, AddressSpecs(address_specs)) - go_package_targets = [ - tgt - for tgt in candidate_targets - if tgt.has_field(GoPackageSources) and tgt.address != this_go_package.address - ] - - # Resolve all of the packages found. - first_party_import_path_to_address = {} - first_party_go_packages = await MultiGet( - Get(ResolvedGoPackage, ResolveGoPackageRequest(tgt.address)) for tgt in go_package_targets - ) - for first_party_go_package in first_party_go_packages: - # Skip packages that are not part of this package's module. - # TODO: This requires that all first-party code in the monorepo be part of the same go_mod. Will need - # figure out how multiple modules in a monorepo can interact. - if first_party_go_package.module_address != this_go_package.module_address: - continue - - address = first_party_go_package.address - if not address: - continue - - first_party_import_path_to_address[first_party_go_package.import_path] = address - - # Loop through all of the imports of this package and add dependencies on other packages and - # external modules. inferred_dependencies = [] for import_path in this_go_package.imports + this_go_package.test_imports: if import_path in std_lib_imports: continue - - # Infer first-party dependencies to other packages in same go_mod. - if import_path in first_party_import_path_to_address: - inferred_dependencies.append(first_party_import_path_to_address[import_path]) - continue - - # Infer third-party dependencies on _go_external_package targets. - candidate_third_party_packages = package_mapping.mapping.get(import_path, ()) - if len(candidate_third_party_packages) > 1: + candidate_packages = package_mapping.mapping.get(import_path, ()) + if len(candidate_packages) > 1: # TODO: Use ExplicitlyProvidedDependencies.maybe_warn_of_ambiguous_dependency_inference standard # way of doing disambiguation. logger.warning( - f"Ambiguous mapping for import path {import_path} on packages at addresses: {candidate_third_party_packages}" + f"Ambiguous mapping for import path {import_path} on packages at addresses: {candidate_packages}" ) - elif len(candidate_third_party_packages) == 1: - inferred_dependencies.append(candidate_third_party_packages[0]) + elif len(candidate_packages) == 1: + inferred_dependencies.append(candidate_packages[0]) else: logger.debug( f"Unable to infer dependency for import path '{import_path}' " - f"in go_package at address '{this_go_package.address}'." + f"in _go_internal_package at address '{this_go_package.address}'." ) return InferredDependencies(inferred_dependencies) class InjectGoExternalPackageDependenciesRequest(InjectDependenciesRequest): - inject_for = GoExternalPackageDependencies + inject_for = GoExternalPackageDependenciesField @rule async def inject_go_external_package_dependencies( request: InjectGoExternalPackageDependenciesRequest, std_lib_imports: GoStdLibImports, - package_mapping: GoImportPathToPackageMapping, + package_mapping: ImportPathToPackages, ) -> InjectedDependencies: addr = request.dependencies_field.address wrapped_target = await Get(WrappedTarget, Address, addr) @@ -206,7 +153,7 @@ async def inject_go_external_package_dependencies( ExternalPkgInfoRequest( module_path=tgt[GoExternalModulePathField].value, version=tgt[GoExternalModuleVersionField].value, - import_path=tgt[GoExternalPackageImportPathField].value, + import_path=tgt[GoImportPathField].value, go_mod_stripped_digest=go_mod_info.stripped_digest, ), ) @@ -216,40 +163,51 @@ async def inject_go_external_package_dependencies( if import_path in std_lib_imports: continue - # Infer third-party dependencies on _go_external_package targets. - candidate_third_party_packages = package_mapping.mapping.get(import_path, ()) - if len(candidate_third_party_packages) > 1: + candidate_packages = package_mapping.mapping.get(import_path, ()) + if len(candidate_packages) > 1: # TODO: Use ExplicitlyProvidedDependencies.maybe_warn_of_ambiguous_dependency_inference standard # way of doing disambiguation. logger.warning( - f"Ambiguous mapping for import path {import_path} on packages at addresses: {candidate_third_party_packages}" + f"Ambiguous mapping for import path {import_path} on packages at addresses: {candidate_packages}" ) - elif len(candidate_third_party_packages) == 1: - inferred_dependencies.append(candidate_third_party_packages[0]) + elif len(candidate_packages) == 1: + inferred_dependencies.append(candidate_packages[0]) else: logger.debug( f"Unable to infer dependency for import path '{import_path}' " - f"in go_external_package at address '{addr}'." + f"in _go_external_package at address '{addr}'." ) return InjectedDependencies(inferred_dependencies) # ----------------------------------------------------------------------------------------------- -# Generate `_go_external_package` targets +# Generate `_go_internal_package` and `_go_external_package` targets # ----------------------------------------------------------------------------------------------- -class GenerateGoExternalPackageTargetsRequest(GenerateTargetsRequest): +class GenerateTargetsFromGoModRequest(GenerateTargetsRequest): generate_from = GoModTarget -@rule(desc="Generate targets for each external package in `go.mod`", level=LogLevel.DEBUG) -async def generate_go_external_package_targets( - request: GenerateGoExternalPackageTargetsRequest, +@rule( + desc="Generate `_go_internal_package` and `_go_external_package` targets from `go_mod` target", + level=LogLevel.DEBUG, +) +async def generate_targets_from_go_mod( + request: GenerateTargetsFromGoModRequest, ) -> GeneratedTargets: generator_addr = request.generator.address - go_mod_info = await Get(GoModInfo, GoModInfoRequest(generator_addr)) + # TODO: Should there be a field on `go_mod` that lets users control what the generating + # sources are? + # TODO: Should we care about there already be owning targets? + _go_paths_glob = ( + f"{generator_addr.spec_path}/**/*.go" if generator_addr.spec_path else "**/*.go" + ) + go_mod_info, go_paths = await MultiGet( + Get(GoModInfo, GoModInfoRequest(generator_addr)), + Get(Paths, PathGlobs([_go_paths_glob])), + ) all_module_info = await MultiGet( Get( ExternalModuleInfo, @@ -262,25 +220,61 @@ async def generate_go_external_package_targets( for module_descriptor in go_mod_info.modules ) - def create_tgt(pkg_info: ExternalPkgInfo) -> GoExternalPackageTarget: + dir_to_filenames = group_by_dir(go_paths.files) + dirs_with_go_files = [] + for dir, filenames in dir_to_filenames.items(): + if any(filename.endswith(".go") for filename in filenames): + dirs_with_go_files.append(dir) + + def create_internal_package_tgt(dir: str) -> GoInternalPackageTarget: + go_mod_spec_path = generator_addr.spec_path + assert dir.startswith( + go_mod_spec_path + ), f"the dir {dir} should start with {go_mod_spec_path}" + + if not go_mod_spec_path: + subpath = dir + elif dir == go_mod_spec_path: + subpath = "" + else: + subpath = dir[len(go_mod_spec_path) + 1 :] + + if subpath: + import_path = f"{go_mod_info.import_path}/{subpath}" + sources = tuple(f"{subpath}/{glob}" for glob in GoInternalPackageSourcesField.default) + else: + import_path = go_mod_info.import_path + sources = GoInternalPackageSourcesField.default + + return GoInternalPackageTarget( + { + GoImportPathField.alias: import_path, + GoInternalPackageSubpathField.alias: subpath, + GoInternalPackageSourcesField.alias: sources, + }, + # E.g. `src/go:mod#./subdir`. + generator_addr.create_generated(f"./{subpath}"), + ) + + internal_pkgs = (create_internal_package_tgt(dir) for dir in dirs_with_go_files) + + def create_external_package_tgt(pkg_info: ExternalPkgInfo) -> GoExternalPackageTarget: return GoExternalPackageTarget( { GoExternalModulePathField.alias: pkg_info.module_path, GoExternalModuleVersionField.alias: pkg_info.version, - GoExternalPackageImportPathField.alias: pkg_info.import_path, + GoImportPathField.alias: pkg_info.import_path, }, # E.g. `src/go:mod#github.com/google/uuid`. generator_addr.create_generated(pkg_info.import_path), ) - return GeneratedTargets( - request.generator, - ( - create_tgt(pkg_info) - for module_info in all_module_info - for pkg_info in module_info.values() - ), + external_pkgs = ( + create_external_package_tgt(pkg_info) + for module_info in all_module_info + for pkg_info in module_info.values() ) + return GeneratedTargets(request.generator, (*internal_pkgs, *external_pkgs)) # ----------------------------------------------------------------------------------------------- @@ -299,7 +293,7 @@ async def determine_main_pkg_for_go_binary( AddressInput, AddressInput.parse(request.field.value, relative_to=addr.spec_path), ) - if not wrapped_specified_tgt.target.has_field(GoPackageSources): + if not wrapped_specified_tgt.target.has_field(GoInternalPackageSourcesField): raise InvalidFieldException( f"The {repr(GoBinaryMainPackageField.alias)} field in target {addr} must point to " "a `go_package` target, but was the address for a " @@ -309,8 +303,11 @@ async def determine_main_pkg_for_go_binary( ) return GoBinaryMainPackage(wrapped_specified_tgt.target.address) + # TODO: fix this to account for `_go_internal_package` being generated. build_dir_targets = await Get(Targets, AddressSpecs([SiblingAddresses(addr.spec_path)])) - internal_pkg_targets = [tgt for tgt in build_dir_targets if tgt.has_field(GoPackageSources)] + internal_pkg_targets = [ + tgt for tgt in build_dir_targets if tgt.has_field(GoInternalPackageSourcesField) + ] if len(internal_pkg_targets) == 1: return GoBinaryMainPackage(internal_pkg_targets[0].address) @@ -355,5 +352,5 @@ def rules(): UnionRule(InferDependenciesRequest, InferGoPackageDependenciesRequest), UnionRule(InjectDependenciesRequest, InjectGoExternalPackageDependenciesRequest), UnionRule(InjectDependenciesRequest, InjectGoBinaryMainDependencyRequest), - UnionRule(GenerateTargetsRequest, GenerateGoExternalPackageTargetsRequest), + UnionRule(GenerateTargetsRequest, GenerateTargetsFromGoModRequest), ) diff --git a/src/python/pants/backend/go/target_type_rules_test.py b/src/python/pants/backend/go/target_type_rules_test.py index a70ae582230..3f774924c05 100644 --- a/src/python/pants/backend/go/target_type_rules_test.py +++ b/src/python/pants/backend/go/target_type_rules_test.py @@ -3,13 +3,14 @@ from __future__ import annotations +import os.path from textwrap import dedent import pytest from pants.backend.go import target_type_rules from pants.backend.go.target_type_rules import ( - GenerateGoExternalPackageTargetsRequest, + GenerateTargetsFromGoModRequest, InferGoPackageDependenciesRequest, InjectGoBinaryMainDependencyRequest, ) @@ -20,12 +21,12 @@ GoBinaryTarget, GoExternalModulePathField, GoExternalModuleVersionField, - GoExternalPackageImportPathField, GoExternalPackageTarget, - GoModSourcesField, + GoImportPathField, + GoInternalPackageSourcesField, + GoInternalPackageSubpathField, + GoInternalPackageTarget, GoModTarget, - GoPackage, - GoPackageSources, ) from pants.backend.go.util_rules import external_pkg, go_mod, go_pkg, sdk from pants.base.exceptions import ResolveError @@ -40,8 +41,6 @@ InferredDependencies, InjectedDependencies, InvalidFieldException, - Target, - Targets, ) from pants.testutil.rule_runner import RuleRunner, engine_error from pants.util.ordered_set import FrozenOrderedSet @@ -60,45 +59,31 @@ def rule_runner() -> RuleRunner: QueryRule(GoBinaryMainPackage, [GoBinaryMainPackageRequest]), QueryRule(InjectedDependencies, [InjectGoBinaryMainDependencyRequest]), ], - target_types=[ - GoPackage, - GoModTarget, - GoExternalPackageTarget, - GoBinaryTarget, - GenericTarget, - ], + target_types=[GoModTarget, GoBinaryTarget, GenericTarget], ) rule_runner.set_options([], env_inherit={"PATH"}) return rule_runner -def assert_go_mod_address(rule_runner: RuleRunner, target: Target, expected_address: Address): - addresses = rule_runner.request(Addresses, [DependenciesRequest(target[Dependencies])]) - targets = rule_runner.request(Targets, [addresses]) - go_mod_targets = [tgt for tgt in targets if tgt.has_field(GoModSourcesField)] - assert len(go_mod_targets) == 1 - assert go_mod_targets[0].address == expected_address - - def test_go_package_dependency_injection(rule_runner: RuleRunner) -> None: rule_runner.write_files( { - "foo/BUILD": "go_mod()\n", - "foo/go.mod": "module foo\n", - "foo/go.sum": "", - "foo/pkg/BUILD": "go_package()\n", - "foo/pkg/foo.go": "package pkg\n", - "foo/bar/BUILD": "go_mod()\ngo_package(name='pkg')\n", - "foo/bar/go.mod": "module bar\n", - "foo/bar/src.go": "package bar\n", + "dir1/go.mod": "module foo", + "dir1/BUILD": "go_mod()", + "dir1/pkg/foo.go": "package pkg", + "dir2/bar/go.mod": "module bar", + "dir2/bar/src.go": "package bar", + "dir2/bar/BUILD": "go_mod()", } ) - target = rule_runner.get_target(Address("foo/pkg", target_name="pkg")) - assert_go_mod_address(rule_runner, target, Address("foo")) + def assert_go_mod(pkg: Address, go_mod: Address) -> None: + tgt = rule_runner.get_target(pkg) + deps = rule_runner.request(Addresses, [DependenciesRequest(tgt[Dependencies])]) + assert set(deps) == {go_mod} - target = rule_runner.get_target(Address("foo/bar", target_name="pkg")) - assert_go_mod_address(rule_runner, target, Address("foo/bar")) + assert_go_mod(Address("dir1", generated_name="./pkg"), Address("dir1")) + assert_go_mod(Address("dir2/bar", generated_name="./"), Address("dir2/bar")) def test_go_package_dependency_inference(rule_runner: RuleRunner) -> None: @@ -122,7 +107,6 @@ def test_go_package_dependency_inference(rule_runner: RuleRunner) -> None: golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= """ ), - "foo/pkg/BUILD": "go_package()\n", "foo/pkg/foo.go": dedent( """\ package pkg @@ -132,7 +116,6 @@ def test_go_package_dependency_inference(rule_runner: RuleRunner) -> None: } """ ), - "foo/cmd/BUILD": "go_package()\n", "foo/cmd/main.go": dedent( """\ package main @@ -147,27 +130,29 @@ def test_go_package_dependency_inference(rule_runner: RuleRunner) -> None: } ) ) - target1 = rule_runner.get_target(Address("foo/cmd")) - inferred_deps_1 = rule_runner.request( - InferredDependencies, [InferGoPackageDependenciesRequest(target1[GoPackageSources])] + tgt1 = rule_runner.get_target(Address("foo", generated_name="./cmd")) + inferred_deps1 = rule_runner.request( + InferredDependencies, + [InferGoPackageDependenciesRequest(tgt1[GoInternalPackageSourcesField])], ) - assert inferred_deps_1.dependencies == FrozenOrderedSet([Address("foo/pkg")]) + assert inferred_deps1.dependencies == FrozenOrderedSet([Address("foo", generated_name="./pkg")]) - target2 = rule_runner.get_target(Address("foo/pkg")) - inferred_deps_2 = rule_runner.request( - InferredDependencies, [InferGoPackageDependenciesRequest(target2[GoPackageSources])] + tgt2 = rule_runner.get_target(Address("foo", generated_name="./pkg")) + inferred_deps2 = rule_runner.request( + InferredDependencies, + [InferGoPackageDependenciesRequest(tgt2[GoInternalPackageSourcesField])], ) - assert inferred_deps_2.dependencies == FrozenOrderedSet( + assert inferred_deps2.dependencies == FrozenOrderedSet( [Address("foo", generated_name="github.com/google/go-cmp/cmp")] ) # ----------------------------------------------------------------------------------------------- -# Generate `_go_external_package` targets +# Generate package targets from `go_mod` # ----------------------------------------------------------------------------------------------- -def test_generate_go_external_package_targets(rule_runner: RuleRunner) -> None: +def test_generate_package_targets(rule_runner: RuleRunner) -> None: rule_runner.write_files( { "src/go/BUILD": "go_mod()\n", @@ -192,19 +177,34 @@ def test_generate_go_external_package_targets(rule_runner: RuleRunner) -> None: golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= """ ), + "src/go/hello.go": "", + "src/go/subdir/f.go": "", + "src/go/another_dir/subdir/another_dir/f.go": "", } ) generator = rule_runner.get_target(Address("src/go")) - generated = rule_runner.request( - GeneratedTargets, [GenerateGoExternalPackageTargetsRequest(generator)] - ) + generated = rule_runner.request(GeneratedTargets, [GenerateTargetsFromGoModRequest(generator)]) - def gen_tgt(mod_path: str, version: str, import_path: str) -> GoExternalPackageTarget: + def gen_internal_tgt(rel_dir: str) -> GoInternalPackageTarget: + return GoInternalPackageTarget( + { + GoImportPathField.alias: ( + os.path.join("example.com/src/go", rel_dir) if rel_dir else "example.com/src/go" + ), + GoInternalPackageSubpathField.alias: rel_dir, + GoInternalPackageSourcesField.alias: tuple( + os.path.join(rel_dir, glob) for glob in GoInternalPackageSourcesField.default + ), + }, + Address("src/go", generated_name=f"./{rel_dir}"), + ) + + def gen_external_tgt(mod_path: str, version: str, import_path: str) -> GoExternalPackageTarget: return GoExternalPackageTarget( { + GoImportPathField.alias: import_path, GoExternalModulePathField.alias: mod_path, GoExternalModuleVersionField.alias: version, - GoExternalPackageImportPathField.alias: import_path, }, Address("src/go", generated_name=import_path), ) @@ -212,37 +212,42 @@ def gen_tgt(mod_path: str, version: str, import_path: str) -> GoExternalPackageT expected = GeneratedTargets( generator, { - gen_tgt("github.com/google/uuid", "v1.2.0", "github.com/google/uuid"), - gen_tgt("github.com/google/go-cmp", "v0.4.0", "github.com/google/go-cmp/cmp"), - gen_tgt("github.com/google/go-cmp", "v0.4.0", "github.com/google/go-cmp/cmp/cmpopts"), - gen_tgt( + gen_internal_tgt(""), + gen_internal_tgt("subdir"), + gen_internal_tgt("another_dir/subdir/another_dir"), + gen_external_tgt("github.com/google/uuid", "v1.2.0", "github.com/google/uuid"), + gen_external_tgt("github.com/google/go-cmp", "v0.4.0", "github.com/google/go-cmp/cmp"), + gen_external_tgt( + "github.com/google/go-cmp", "v0.4.0", "github.com/google/go-cmp/cmp/cmpopts" + ), + gen_external_tgt( "github.com/google/go-cmp", "v0.4.0", "github.com/google/go-cmp/cmp/internal/diff" ), - gen_tgt( + gen_external_tgt( "github.com/google/go-cmp", "v0.4.0", "github.com/google/go-cmp/cmp/internal/flags" ), - gen_tgt( + gen_external_tgt( "github.com/google/go-cmp", "v0.4.0", "github.com/google/go-cmp/cmp/internal/function", ), - gen_tgt( + gen_external_tgt( "github.com/google/go-cmp", "v0.4.0", "github.com/google/go-cmp/cmp/internal/testprotos", ), - gen_tgt( + gen_external_tgt( "github.com/google/go-cmp", "v0.4.0", "github.com/google/go-cmp/cmp/internal/teststructs", ), - gen_tgt( + gen_external_tgt( "github.com/google/go-cmp", "v0.4.0", "github.com/google/go-cmp/cmp/internal/value" ), - gen_tgt( + gen_external_tgt( "golang.org/x/xerrors", "v0.0.0-20191204190536-9bdfabe68543", "golang.org/x/xerrors" ), - gen_tgt( + gen_external_tgt( "golang.org/x/xerrors", "v0.0.0-20191204190536-9bdfabe68543", "golang.org/x/xerrors/internal", @@ -250,7 +255,8 @@ def gen_tgt(mod_path: str, version: str, import_path: str) -> GoExternalPackageT }, ) assert list(generated.keys()) == list(expected.keys()) - assert generated == expected + for addr, tgt in generated.items(): + assert tgt == expected[addr] # ----------------------------------------------------------------------------------------------- @@ -258,6 +264,7 @@ def gen_tgt(mod_path: str, version: str, import_path: str) -> GoExternalPackageT # ----------------------------------------------------------------------------------------------- +@pytest.mark.xfail def test_determine_main_pkg_for_go_binary(rule_runner: RuleRunner) -> None: rule_runner.write_files( { diff --git a/src/python/pants/backend/go/target_types.py b/src/python/pants/backend/go/target_types.py index 5c4cf6b1100..e981eb60688 100644 --- a/src/python/pants/backend/go/target_types.py +++ b/src/python/pants/backend/go/target_types.py @@ -21,36 +21,15 @@ ) -class GoSources(Sources): - expected_file_extensions = (".go", ".s") - - -# ----------------------------------------------------------------------------------------------- -# `go_package` target -# ----------------------------------------------------------------------------------------------- - - -class GoPackageSources(GoSources): - default = ("*.go", "*.s") - - -class GoImportPath(StringField): +class GoImportPathField(StringField): alias = "import_path" - help = "Import path in Go code to import this package or module." - - -class GoPackageDependencies(Dependencies): - pass - - -class GoPackage(Target): - alias = "go_package" - core_fields = (*COMMON_TARGET_FIELDS, GoPackageDependencies, GoPackageSources, GoImportPath) - help = "A single Go package." + help = "Import path in Go code to import this package." + required = True + value: str # ----------------------------------------------------------------------------------------------- -# `go_mod` target +# `go_mod` target generator # ----------------------------------------------------------------------------------------------- @@ -88,14 +67,59 @@ def validate_resolved_files(self, files: Sequence[str]) -> None: ) +# TODO: This field probably shouldn't be registered. +class GoModDependenciesField(Dependencies): + alias = "_dependencies" + + class GoModTarget(Target): alias = "go_mod" + core_fields = (*COMMON_TARGET_FIELDS, GoModDependenciesField, GoModSourcesField) + help = ( + "A first-party Go module corresponding to a `go.mod` file.\n\n" + "Generates `_go_internal_package` targets for each subdirectory with a `.go` file, and " + "generates `_go_external_package` targets based on the `require` directives in your " + "`go.mod`.\n\n" + "If you have external packages, make sure you have an up-to-date `go.sum`. Run " + "`go mod tidy` directly to update your `go.mod` and `go.sum`." + ) + + +# ----------------------------------------------------------------------------------------------- +# `_go_internal_package` target +# ----------------------------------------------------------------------------------------------- + + +class GoInternalPackageSourcesField(Sources): + default = ("*.go", "*.s") + expected_file_extensions = (".go", ".s") + + +class GoInternalPackageDependenciesField(Dependencies): + pass + + +class GoInternalPackageSubpathField(StringField): + alias = "subpath" + help = ( + "The path from the owning `go.mod` to this package's directory, e.g. `subdir`.\n\n" + "Should not include a leading `./`. If the package is in the same directory as the " + "`go.mod`, use the empty string." + ) + required = True + value: str + + +class GoInternalPackageTarget(Target): + alias = "_go_internal_package" core_fields = ( *COMMON_TARGET_FIELDS, - Dependencies, - GoModSourcesField, + GoImportPathField, + GoInternalPackageSubpathField, + GoInternalPackageDependenciesField, + GoInternalPackageSourcesField, ) - help = "A first-party Go module corresponding to a `go.mod` file." + help = "A single Go package." # ----------------------------------------------------------------------------------------------- @@ -103,7 +127,7 @@ class GoModTarget(Target): # ----------------------------------------------------------------------------------------------- -class GoExternalPackageDependencies(Dependencies): +class GoExternalPackageDependenciesField(Dependencies): pass @@ -124,19 +148,14 @@ class GoExternalModuleVersionField(StringField): value: str -class GoExternalPackageImportPathField(GoImportPath): - required = True - value: str - - class GoExternalPackageTarget(Target): alias = "_go_external_package" core_fields = ( *COMMON_TARGET_FIELDS, - GoExternalPackageDependencies, + GoExternalPackageDependenciesField, GoExternalModulePathField, GoExternalModuleVersionField, - GoExternalPackageImportPathField, + GoImportPathField, ) help = "A package from a third-party Go module." diff --git a/src/python/pants/backend/go/util_rules/assembly_integration_test.py b/src/python/pants/backend/go/util_rules/assembly_integration_test.py index 29d1d9d3eda..cd69347d31a 100644 --- a/src/python/pants/backend/go/util_rules/assembly_integration_test.py +++ b/src/python/pants/backend/go/util_rules/assembly_integration_test.py @@ -12,7 +12,7 @@ from pants.backend.go import target_type_rules from pants.backend.go.goals import package_binary from pants.backend.go.goals.package_binary import GoBinaryFieldSet -from pants.backend.go.target_types import GoBinaryTarget, GoModTarget, GoPackage +from pants.backend.go.target_types import GoBinaryTarget, GoModTarget from pants.backend.go.util_rules import ( assembly, build_go_pkg, @@ -50,7 +50,7 @@ def rule_runner() -> RuleRunner: *sdk.rules(), QueryRule(BuiltPackage, (GoBinaryFieldSet,)), ], - target_types=[GoBinaryTarget, GoPackage, GoModTarget], + target_types=[GoBinaryTarget, GoModTarget], ) rule_runner.set_options([], env_inherit={"PATH"}) return rule_runner @@ -113,7 +113,6 @@ def test_build_package_with_assembly(rule_runner: RuleRunner) -> None: "BUILD": dedent( """\ go_mod(name="mod") - go_package(name="pkg") go_binary(name="bin") """ ), diff --git a/src/python/pants/backend/go/util_rules/build_go_pkg.py b/src/python/pants/backend/go/util_rules/build_go_pkg.py index 7eeebc61714..30526f74310 100644 --- a/src/python/pants/backend/go/util_rules/build_go_pkg.py +++ b/src/python/pants/backend/go/util_rules/build_go_pkg.py @@ -9,8 +9,9 @@ from pants.backend.go.target_types import ( GoExternalModulePathField, GoExternalModuleVersionField, - GoExternalPackageImportPathField, - GoPackageSources, + GoImportPathField, + GoInternalPackageSourcesField, + GoInternalPackageSubpathField, ) from pants.backend.go.util_rules.assembly import ( AssemblyPostCompilation, @@ -74,19 +75,19 @@ async def build_go_package(request: BuildGoPackageRequest) -> BuiltGoPackage: _source_files, _resolved_package = await MultiGet( Get( SourceFiles, - SourceFilesRequest((target[GoPackageSources],)), + SourceFilesRequest((target[GoInternalPackageSourcesField],)), ), Get(ResolvedGoPackage, ResolveGoPackageRequest(address=target.address)), ) source_files_digest = _source_files.snapshot.digest - source_files_subpath = target.address.spec_path + source_files_subpath = target[GoInternalPackageSubpathField].value original_import_path = _resolved_package.import_path go_files = _resolved_package.go_files s_files = _resolved_package.s_files elif is_third_party_package_target(target): - original_import_path = target[GoExternalPackageImportPathField].value + original_import_path = target[GoImportPathField].value _module_path = target[GoExternalModulePathField].value source_files_subpath = original_import_path[len(_module_path) :] diff --git a/src/python/pants/backend/go/util_rules/build_go_pkg_test.py b/src/python/pants/backend/go/util_rules/build_go_pkg_test.py index 8e8ec5391ad..1726ae10aa8 100644 --- a/src/python/pants/backend/go/util_rules/build_go_pkg_test.py +++ b/src/python/pants/backend/go/util_rules/build_go_pkg_test.py @@ -9,7 +9,7 @@ import pytest from pants.backend.go import target_type_rules -from pants.backend.go.target_types import GoModTarget, GoPackage +from pants.backend.go.target_types import GoModTarget from pants.backend.go.util_rules import ( assembly, build_go_pkg, @@ -45,7 +45,7 @@ def rule_runner() -> RuleRunner: *target_type_rules.rules(), QueryRule(BuiltGoPackage, [BuildGoPackageRequest]), ], - target_types=[GoPackage, GoModTarget], + target_types=[GoModTarget], ) rule_runner.set_options([], env_inherit={"PATH"}) return rule_runner @@ -84,16 +84,13 @@ def test_build_internal_pkg(rule_runner: RuleRunner) -> None: } """ ), - "BUILD": dedent( - """\ - go_mod(name="mod") - go_package(name="pkg") - """ - ), + "BUILD": "go_mod(name='mod')", } ) assert_built( - rule_runner, Address("", target_name="pkg"), expected_import_paths=["example.com/greeter"] + rule_runner, + Address("", target_name="mod", generated_name="./"), + expected_import_paths=["example.com/greeter"], ) @@ -139,7 +136,6 @@ def test_build_dependencies(rule_runner: RuleRunner) -> None: } """ ), - "greeter/quoter/BUILD": "go_package()", "greeter/lib.go": dedent( """\ package greeter @@ -156,7 +152,6 @@ def test_build_dependencies(rule_runner: RuleRunner) -> None: } """ ), - "greeter/BUILD": "go_package()", "main.go": dedent( """\ package main @@ -184,7 +179,6 @@ def test_build_dependencies(rule_runner: RuleRunner) -> None: "BUILD": dedent( """\ go_mod(name='mod') - go_package(name='pkg') """ ), } @@ -205,17 +199,25 @@ def test_build_dependencies(rule_runner: RuleRunner) -> None: ) quoter_import_path = "example.com/project/greeter/quoter" - assert_built(rule_runner, Address("greeter/quoter"), expected_import_paths=[quoter_import_path]) + assert_built( + rule_runner, + Address("", target_name="mod", generated_name="./greeter/quoter"), + expected_import_paths=[quoter_import_path], + ) greeter_import_paths = [ "example.com/project/greeter", quoter_import_path, *xerrors_import_paths, ] - assert_built(rule_runner, Address("greeter"), expected_import_paths=greeter_import_paths) + assert_built( + rule_runner, + Address("", target_name="mod", generated_name="./greeter"), + expected_import_paths=greeter_import_paths, + ) assert_built( rule_runner, - Address("", target_name="pkg"), + Address("", target_name="mod", generated_name="./"), expected_import_paths=["example.com/project", *greeter_import_paths], ) diff --git a/src/python/pants/backend/go/util_rules/go_mod_test.py b/src/python/pants/backend/go/util_rules/go_mod_test.py index 62dd0872efb..5fbe3e94920 100644 --- a/src/python/pants/backend/go/util_rules/go_mod_test.py +++ b/src/python/pants/backend/go/util_rules/go_mod_test.py @@ -7,7 +7,7 @@ import pytest -from pants.backend.go.target_types import GoModTarget, GoPackage +from pants.backend.go.target_types import GoModTarget from pants.backend.go.util_rules import go_mod, sdk from pants.backend.go.util_rules.go_mod import GoModInfo, GoModInfoRequest from pants.build_graph.address import Address @@ -23,7 +23,7 @@ def rule_runner() -> RuleRunner: *go_mod.rules(), QueryRule(GoModInfo, [GoModInfoRequest]), ], - target_types=[GoPackage, GoModTarget], + target_types=[GoModTarget], ) rule_runner.set_options([], env_inherit={"PATH"}) return rule_runner @@ -63,17 +63,10 @@ def test_go_mod_info(rule_runner: RuleRunner) -> None: """ ), "foo/main.go": "package main\nfunc main() { }\n", - "foo/BUILD": dedent( - """\ - go_mod(name='mod') - go_package(name='pkg') - """ - ), + "foo/BUILD": "go_mod()", } ) - go_mod_info = rule_runner.request( - GoModInfo, [GoModInfoRequest(Address("foo", target_name="mod"))] - ) + go_mod_info = rule_runner.request(GoModInfo, [GoModInfoRequest(Address("foo"))]) assert go_mod_info.import_path == "go.example.com/foo" assert go_mod_info.modules assert any( diff --git a/src/python/pants/backend/go/util_rules/go_pkg.py b/src/python/pants/backend/go/util_rules/go_pkg.py index fe56956d08d..35a0354a947 100644 --- a/src/python/pants/backend/go/util_rules/go_pkg.py +++ b/src/python/pants/backend/go/util_rules/go_pkg.py @@ -7,9 +7,10 @@ from dataclasses import dataclass from pants.backend.go.target_types import ( - GoExternalPackageDependencies, - GoImportPath, - GoPackageSources, + GoExternalPackageDependenciesField, + GoImportPathField, + GoInternalPackageSourcesField, + GoInternalPackageSubpathField, ) from pants.backend.go.util_rules.go_mod import ( GoModInfo, @@ -166,11 +167,11 @@ def error_to_string(d: dict) -> str: def is_first_party_package_target(tgt: Target) -> bool: - return tgt.has_field(GoPackageSources) + return tgt.has_field(GoInternalPackageSourcesField) def is_third_party_package_target(tgt: Target) -> bool: - return tgt.has_field(GoExternalPackageDependencies) + return tgt.has_field(GoExternalPackageDependenciesField) @rule @@ -182,46 +183,30 @@ async def resolve_go_package( Get(OwningGoMod, OwningGoModRequest(request.address)), ) target = wrapped_target.target - - go_mod_spec_path = owning_go_mod.address.spec_path - assert request.address.spec_path.startswith(go_mod_spec_path) - spec_subpath = request.address.spec_path[len(go_mod_spec_path) :] + subpath = target[GoInternalPackageSubpathField].value go_mod_info, pkg_sources = await MultiGet( Get(GoModInfo, GoModInfoRequest(owning_go_mod.address)), - Get(HydratedSources, HydrateSourcesRequest(target[GoPackageSources])), + Get(HydratedSources, HydrateSourcesRequest(target[GoInternalPackageSourcesField])), ) input_digest = await Get( Digest, MergeDigests([pkg_sources.snapshot.digest, go_mod_info.digest]) ) - # Compute the import_path for this go_package. - import_path_field = target.get(GoImportPath) - if import_path_field and import_path_field.value: - # Use any explicit import path set on the `go_package` target. - import_path = import_path_field.value - else: - # Otherwise infer the import path from the owning `go_mod` target. The inferred import - # path will be the module's import path plus any subdirectories in the spec_path - # between the go_mod and go_package target. - import_path = f"{go_mod_info.import_path}" - if spec_subpath: - import_path += spec_subpath if spec_subpath.startswith("/") else f"/{spec_subpath}" - result = await Get( ProcessResult, GoSdkProcess( input_digest=input_digest, - command=("list", "-json", f"./{spec_subpath}"), + command=("list", "-json", f"./{subpath}"), description="Resolve go_package metadata.", - working_dir=go_mod_spec_path, + working_dir=owning_go_mod.address.spec_path, ), ) metadata = json.loads(result.stdout) return ResolvedGoPackage.from_metadata( metadata, - import_path=import_path, + import_path=target[GoImportPathField].value, address=request.address, module_address=owning_go_mod.address, ) diff --git a/src/python/pants/backend/go/util_rules/go_pkg_test.py b/src/python/pants/backend/go/util_rules/go_pkg_test.py index 099fc9be279..b4c9bbf11a8 100644 --- a/src/python/pants/backend/go/util_rules/go_pkg_test.py +++ b/src/python/pants/backend/go/util_rules/go_pkg_test.py @@ -3,12 +3,13 @@ from __future__ import annotations -import textwrap +from textwrap import dedent import pytest -from pants.backend.go.target_types import GoModTarget, GoPackage -from pants.backend.go.util_rules import go_mod, go_pkg, sdk +from pants.backend.go import target_type_rules +from pants.backend.go.target_types import GoModTarget +from pants.backend.go.util_rules import external_pkg, go_mod, go_pkg, sdk from pants.backend.go.util_rules.go_pkg import ResolvedGoPackage, ResolveGoPackageRequest from pants.build_graph.address import Address from pants.engine.rules import QueryRule @@ -22,9 +23,11 @@ def rule_runner() -> RuleRunner: *go_mod.rules(), *go_pkg.rules(), *sdk.rules(), + *external_pkg.rules(), + *target_type_rules.rules(), QueryRule(ResolvedGoPackage, [ResolveGoPackageRequest]), ], - target_types=[GoPackage, GoModTarget], + target_types=[GoModTarget], ) rule_runner.set_options([], env_inherit={"PATH"}) return rule_runner @@ -34,14 +37,13 @@ def test_resolve_go_package(rule_runner: RuleRunner) -> None: rule_runner.write_files( { "foo/BUILD": "go_mod()\n", - "foo/go.mod": textwrap.dedent( + "foo/go.mod": dedent( """\ module go.example.com/foo go 1.17 """ ), - "foo/pkg/BUILD": "go_package()\n", - "foo/pkg/foo.go": textwrap.dedent( + "foo/pkg/foo.go": dedent( """\ package pkg func Grok() string { @@ -49,8 +51,7 @@ def test_resolve_go_package(rule_runner: RuleRunner) -> None: } """ ), - "foo/cmd/BUILD": "go_package()\n", - "foo/cmd/main.go": textwrap.dedent( + "foo/cmd/main.go": dedent( """\ package main import ( @@ -62,7 +63,7 @@ def test_resolve_go_package(rule_runner: RuleRunner) -> None: } """ ), - "foo/cmd/bar_test.go": textwrap.dedent( + "foo/cmd/bar_test.go": dedent( """\ package main import "testing" @@ -72,12 +73,12 @@ def test_resolve_go_package(rule_runner: RuleRunner) -> None: } ) resolved_go_package = rule_runner.request( - ResolvedGoPackage, [ResolveGoPackageRequest(Address("foo/cmd"))] + ResolvedGoPackage, [ResolveGoPackageRequest(Address("foo", generated_name="./cmd"))] ) # Compare field-by-field rather than with a `ResolvedGoPackage` instance because # `dependency_import_paths` is so verbose. - assert resolved_go_package.address == Address("foo/cmd") + assert resolved_go_package.address == Address("foo", generated_name="./cmd") assert resolved_go_package.import_path == "go.example.com/foo/cmd" assert resolved_go_package.module_address == Address("foo") assert resolved_go_package.package_name == "main" diff --git a/testprojects/src/go/pants_test/BUILD b/testprojects/src/go/pants_test/BUILD index cbdeec03429..c085f1fa980 100644 --- a/testprojects/src/go/pants_test/BUILD +++ b/testprojects/src/go/pants_test/BUILD @@ -1,5 +1,4 @@ # Copyright 2021 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). -go_package() go_binary(name="bin") diff --git a/testprojects/src/go/pants_test/bar/BUILD b/testprojects/src/go/pants_test/bar/BUILD deleted file mode 100644 index 72ecb6dbb8f..00000000000 --- a/testprojects/src/go/pants_test/bar/BUILD +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md). -# Licensed under the Apache License, Version 2.0 (see LICENSE). - -go_package() -