async def relocate_files(request: RelocateFilesViaCodegenRequest) -> GeneratedSources: # Unlike normal codegen, we operate the on the sources of the `files_targets` field, not the # `sources` of the original `relocated_sources` target. # TODO(#10915): using `await Get(Addresses, UnparsedAddressInputs)` causes a graph failure. original_files_targets = await MultiGet( Get( WrappedTarget, AddressInput, AddressInput.parse(v, relative_to=request.protocol_target.address.spec_path), ) for v in ( request.protocol_target.get(RelocatedFilesOriginalTargets) .to_unparsed_address_inputs() .values ) ) original_files_sources = await MultiGet( Get(HydratedSources, HydrateSourcesRequest(wrapped_tgt.target.get(Sources))) for wrapped_tgt in original_files_targets ) snapshot = await Get( Snapshot, MergeDigests(sources.snapshot.digest for sources in original_files_sources) ) src_val = request.protocol_target.get(RelocatedFilesSrcField).value dest_val = request.protocol_target.get(RelocatedFilesDestField).value if src_val: snapshot = await Get(Snapshot, RemovePrefix(snapshot.digest, src_val)) if dest_val: snapshot = await Get(Snapshot, AddPrefix(snapshot.digest, dest_val)) return GeneratedSources(snapshot)
def calculate_specs( options_bootstrapper: OptionsBootstrapper, options: Options, session: SchedulerSession, *, build_root: Optional[str] = None, ) -> Specs: """Determine the specs for a given Pants run.""" build_root = build_root or get_buildroot() specs = SpecsParser(build_root).parse_specs(options.specs) changed_options = ChangedOptions.from_options(options.for_scope("changed")) logger.debug("specs are: %s", specs) logger.debug("changed_options are: %s", changed_options) if specs.provided and changed_options.provided: changed_name = "--changed-since" if changed_options.since else "--changed-diffspec" if specs.filesystem_specs and specs.address_specs: specs_description = "target and file arguments" elif specs.filesystem_specs: specs_description = "file arguments" else: specs_description = "target arguments" raise InvalidSpecConstraint( f"You used `{changed_name}` at the same time as using {specs_description}. Please " "use only one.") if not changed_options.provided: return specs git = get_git() if not git: raise InvalidSpecConstraint( "The `--changed-*` options are only available if Git is used for the repository." ) changed_request = ChangedRequest( sources=tuple(changed_options.changed_files(git)), dependees=changed_options.dependees, ) (changed_addresses, ) = session.product_request( ChangedAddresses, [Params(changed_request, options_bootstrapper)]) logger.debug("changed addresses: %s", changed_addresses) address_specs = [] for address in cast(ChangedAddresses, changed_addresses): address_input = AddressInput.parse(address.spec) address_specs.append( AddressLiteralSpec( path_component=address_input.path_component, # NB: AddressInput.target_component may be None, but AddressLiteralSpec expects a # string. target_component=address_input.target_component or address.target_name, )) return Specs(AddressSpecs(address_specs, filter_by_global_options=True), FilesystemSpecs([]))
async def infer_smalltalk_dependencies(request: InferSmalltalkDependencies) -> InferredDependencies: # To demo an inference rule, we simply treat each `sources` file to contain a list of # addresses, one per line. hydrated_sources = await Get(HydratedSources, HydrateSourcesRequest(request.sources_field)) digest_contents = await Get(DigestContents, Digest, hydrated_sources.snapshot.digest) all_lines = itertools.chain.from_iterable( file_content.content.decode().splitlines() for file_content in digest_contents ) resolved = await MultiGet( Get(Address, AddressInput, AddressInput.parse(line)) for line in all_lines ) return InferredDependencies(resolved)
async def resolve_unparsed_address_inputs( request: UnparsedAddressInputs, subproject_roots: SubprojectRoots) -> Addresses: addresses = await MultiGet( Get( Address, AddressInput, AddressInput.parse(v, relative_to=request.relative_to, subproject_roots=subproject_roots), ) for v in request.values) return Addresses(addresses)
async def resolve_unparsed_address_inputs( request: UnparsedAddressInputs, global_options: GlobalOptions) -> Addresses: addresses = await MultiGet( Get( Address, AddressInput, AddressInput.parse( v, relative_to=request.relative_to, subproject_roots=global_options.options.subproject_roots, ), ) for v in request.values) return Addresses(addresses)
async def transitive_targets_lite(request: TransitiveTargetsRequestLite) -> TransitiveTargets: roots_as_targets = await Get(Targets, Addresses(request.roots)) visited: OrderedSet[Target] = OrderedSet() queued = FrozenOrderedSet(roots_as_targets) dependency_mapping: Dict[Address, Tuple[Address, ...]] = {} while queued: direct_dependencies_addresses_per_tgt = await MultiGet( Get(Addresses, DependenciesRequestLite(tgt.get(Dependencies))) for tgt in queued ) direct_dependencies_per_tgt = [] for addresses_per_tgt in direct_dependencies_addresses_per_tgt: wrapped_tgts = await MultiGet( Get(WrappedTarget, Address, addr) for addr in addresses_per_tgt ) direct_dependencies_per_tgt.append( tuple(wrapped_t.target for wrapped_t in wrapped_tgts) ) dependency_mapping.update( zip( (t.address for t in queued), (tuple(t.address for t in deps) for deps in direct_dependencies_per_tgt), ) ) queued = FrozenOrderedSet( itertools.chain.from_iterable(direct_dependencies_per_tgt) ).difference(visited) visited.update(queued) # NB: We use `roots_as_targets` to get the root addresses, rather than `request.roots`. This # is because expanding from the `Addresses` -> `Targets` may have resulted in generated # subtargets being used, so we need to use `roots_as_targets` to have this expansion. _detect_cycles(tuple(t.address for t in roots_as_targets), dependency_mapping) # Apply any transitive excludes (`!!` ignores). wrapped_transitive_excludes = await MultiGet( Get( WrappedTarget, AddressInput, AddressInput.parse(addr, relative_to=tgt.address.spec_path) ) for tgt in (*roots_as_targets, *visited) for addr in tgt.get(Dependencies).unevaluated_transitive_excludes.values ) transitive_excludes = FrozenOrderedSet( wrapped_t.target for wrapped_t in wrapped_transitive_excludes ) return TransitiveTargets( tuple(roots_as_targets), FrozenOrderedSet(visited.difference(transitive_excludes)) )
async def determine_main_pkg_for_go_binary( request: GoBinaryMainPackageRequest, ) -> GoBinaryMainPackage: addr = request.field.address if request.field.value: wrapped_specified_tgt = await Get( WrappedTarget, AddressInput, AddressInput.parse(request.field.value, relative_to=addr.spec_path), ) if not wrapped_specified_tgt.target.has_field( GoFirstPartyPackageSourcesField): raise InvalidFieldException( f"The {repr(GoBinaryMainPackageField.alias)} field in target {addr} must point to " "a `go_first_party_package` target, but was the address for a " f"`{wrapped_specified_tgt.target.alias}` target.\n\n" "Hint: you should normally not specify this field so that Pants will find the " "`go_first_party_package` target for you. (Pants generates " "`go_first_party_package` targets based on the `go_mod` target)." ) return GoBinaryMainPackage(wrapped_specified_tgt.target.address) candidate_targets = await Get( Targets, AddressSpecs([SiblingAddresses(addr.spec_path)])) relevant_pkg_targets = [ tgt for tgt in candidate_targets if tgt.has_field(GoFirstPartyPackageSourcesField) and tgt.residence_dir == addr.spec_path ] if len(relevant_pkg_targets) == 1: return GoBinaryMainPackage(relevant_pkg_targets[0].address) wrapped_tgt = await Get(WrappedTarget, Address, addr) alias = wrapped_tgt.target.alias if not relevant_pkg_targets: raise ResolveError( f"The `{alias}` target {addr} requires that there is a `go_first_party_package` " f"target for its directory {addr.spec_path}, but none were found.\n\n" "Have you added a `go_mod` target (which will generate `go_first_party_package` " "targets)?") raise ResolveError( f"There are multiple `go_first_party_package` targets for the same directory of the " f"`{alias}` target {addr}: {addr.spec_path}. It is ambiguous what to use as the `main` " "package.\n\n" f"To fix, please either set the `main` field for `{addr} or remove these " "`go_first_party_package` targets so that only one remains: " f"{sorted(tgt.address.spec for tgt in relevant_pkg_targets)}")
def maybe_address(val: str, renames: MacroRenames, *, relative_to: str | None) -> Address | None: # All macros generate targets with a `name`, so we know they must have `:`. We know they # also can't have `#` because they're not generated targets syntax. if ":" not in val or "#" in val: return None try: # We assume that all addresses are normal addresses, rather than file addresses, as # we know that none of the generated targets will be file addresses. That is, we can # ignore file addresses. addr = AddressInput.parse(val, relative_to=relative_to).dir_to_address() except (AddressParseException, InvalidAddress): return None return addr if addr in renames.generated else None
async def resolve_dependencies(request: DependenciesRequest, union_membership: UnionMembership, global_options: GlobalOptions) -> Addresses: explicitly_provided = await Get(ExplicitlyProvidedDependencies, DependenciesRequest, request) # Inject any dependencies. This is determined by the `request.field` class. For example, if # there is a rule to inject for FortranDependencies, then FortranDependencies and any subclass # of FortranDependencies will use that rule. inject_request_types = union_membership.get(InjectDependenciesRequest) injected = await MultiGet( Get(InjectedDependencies, InjectDependenciesRequest, inject_request_type(request.field)) for inject_request_type in inject_request_types if isinstance(request.field, inject_request_type.inject_for)) inference_request_types = union_membership.get(InferDependenciesRequest) inferred: Tuple[InferredDependencies, ...] = () if inference_request_types: # Dependency inference is solely determined by the `Sources` field for a Target, so we # re-resolve the original target to inspect its `Sources` field, if any. wrapped_tgt = await Get(WrappedTarget, Address, request.field.address) sources_field = wrapped_tgt.target.get(Sources) relevant_inference_request_types = [ inference_request_type for inference_request_type in inference_request_types if isinstance(sources_field, inference_request_type.infer_from) ] inferred = await MultiGet( Get( InferredDependencies, InferDependenciesRequest, inference_request_type(sources_field), ) for inference_request_type in relevant_inference_request_types) # If this is a BUILD target, or no dependency inference implementation can infer dependencies on # a file address's sibling files, then we inject dependencies on all the BUILD target's # generated subtargets. subtarget_addresses: Tuple[Address, ...] = () no_sibling_file_deps_inferrable = not inferred or all( inferred_deps.sibling_dependencies_inferrable is False for inferred_deps in inferred) if not request.field.address.is_file_target or no_sibling_file_deps_inferrable: subtargets = await Get( Subtargets, Address, request.field.address.maybe_convert_to_build_target()) subtarget_addresses = tuple(t.address for t in subtargets.subtargets if t.address != request.field.address) # If the target has `SpecialCasedDependencies`, such as the `archive` target having # `files` and `packages` fields, then we possibly include those too. We don't want to always # include those dependencies because they should often be excluded from the result due to # being handled elsewhere in the calling code. special_cased: Tuple[Address, ...] = () if request.include_special_cased_deps: wrapped_tgt = await Get(WrappedTarget, Address, request.field.address) # Unlike normal, we don't use `tgt.get()` because there may be >1 subclass of # SpecialCasedDependencies. special_cased_fields = tuple( field for field in wrapped_tgt.target.field_values.values() if isinstance(field, SpecialCasedDependencies)) # We can't use the normal `Get(Addresses, UnparsedAddressInputs)` due to a graph cycle. special_cased = await MultiGet( Get( Address, AddressInput, AddressInput.parse( addr, relative_to=request.field.address.spec_path, subproject_roots=global_options.options.subproject_roots, ), ) for special_cased_field in special_cased_fields for addr in special_cased_field.to_unparsed_address_inputs().values ) result = { addr for addr in ( *subtarget_addresses, *explicitly_provided.includes, *itertools.chain.from_iterable(injected), *itertools.chain.from_iterable(inferred), *special_cased, ) if addr not in explicitly_provided.ignores } return Addresses(sorted(result))
async def inject_dependencies(_: InjectProtobufDependencies, protoc: Protoc) -> InjectedDependencies: addresses = await MultiGet( Get(Address, AddressInput, AddressInput.parse(addr)) for addr in protoc.runtime_targets) return InjectedDependencies(addresses)
async def resolve_dependencies( request: DependenciesRequest, target_types_to_generate_requests: TargetTypesToGenerateTargetsRequests, union_membership: UnionMembership, subproject_roots: SubprojectRoots, ) -> Addresses: wrapped_tgt, explicitly_provided = await MultiGet( Get( WrappedTarget, WrappedTargetRequest(request.field.address, description_of_origin="<infallible>"), ), Get(ExplicitlyProvidedDependencies, DependenciesRequest, request), ) tgt = wrapped_tgt.target # Inject any dependencies (based on `Dependencies` field rather than `SourcesField`). inject_request_types = union_membership.get(InjectDependenciesRequest) injected = await MultiGet( Get(InjectedDependencies, InjectDependenciesRequest, inject_request_type(request.field)) for inject_request_type in inject_request_types if isinstance(request.field, inject_request_type.inject_for)) # Infer any dependencies (based on `SourcesField` field). inference_request_types = union_membership.get(InferDependenciesRequest) inferred: tuple[InferredDependencies, ...] = () if inference_request_types: sources_field = tgt.get(SourcesField) relevant_inference_request_types = [ inference_request_type for inference_request_type in inference_request_types if isinstance(sources_field, inference_request_type.infer_from) ] inferred = await MultiGet( Get( InferredDependencies, InferDependenciesRequest, inference_request_type(sources_field), ) for inference_request_type in relevant_inference_request_types) # If it's a target generator, inject dependencies on all of its generated targets. generated_addresses: tuple[Address, ...] = () if target_types_to_generate_requests.is_generator( tgt) and not tgt.address.is_generated_target: parametrizations = await Get( _TargetParametrizations, _TargetParametrizationsRequest( tgt.address.maybe_convert_to_target_generator(), description_of_origin= (f"the target generator {tgt.address.maybe_convert_to_target_generator()}" ), ), ) generated_addresses = tuple( parametrizations.generated_for(tgt.address).keys()) # If the target is parametrized, see whether any explicitly provided dependencies are also # parametrized, but with partial/no parameters. If so, fill them in. explicitly_provided_includes: Iterable[ Address] = explicitly_provided.includes if request.field.address.is_parametrized and explicitly_provided_includes: explicit_dependency_parametrizations = await MultiGet( Get( _TargetParametrizations, _TargetParametrizationsRequest( address.maybe_convert_to_target_generator(), description_of_origin= (f"the `{request.field.alias}` field of the target {tgt.address}" ), ), ) for address in explicitly_provided_includes) explicitly_provided_includes = [ parametrizations.get_subset(address, tgt).address for address, parametrizations in zip( explicitly_provided_includes, explicit_dependency_parametrizations) ] # If the target has `SpecialCasedDependencies`, such as the `archive` target having # `files` and `packages` fields, then we possibly include those too. We don't want to always # include those dependencies because they should often be excluded from the result due to # being handled elsewhere in the calling code. special_cased: tuple[Address, ...] = () if request.include_special_cased_deps: # Unlike normal, we don't use `tgt.get()` because there may be >1 subclass of # SpecialCasedDependencies. special_cased_fields = tuple( field for field in tgt.field_values.values() if isinstance(field, SpecialCasedDependencies)) # We can't use the normal `Get(Addresses, UnparsedAddressInputs)` due to a graph cycle. special_cased = await MultiGet( Get( Address, AddressInput, AddressInput.parse( addr, relative_to=tgt.address.spec_path, subproject_roots=subproject_roots, description_of_origin= (f"the `{special_cased_field.alias}` field from the target {tgt.address}" ), ), ) for special_cased_field in special_cased_fields for addr in special_cased_field.to_unparsed_address_inputs().values ) result = Addresses( sorted({ addr for addr in ( *generated_addresses, *explicitly_provided_includes, *itertools.chain.from_iterable(injected), *itertools.chain.from_iterable(inferred), *special_cased, ) if addr not in explicitly_provided.ignores })) # Validate dependencies. _ = await MultiGet( Get( ValidatedDependencies, ValidateDependenciesRequest, vd_request_type(vd_request_type.field_set_type.create(tgt), result), # type: ignore[misc] ) for vd_request_type in union_membership.get( ValidateDependenciesRequest) if vd_request_type.field_set_type.is_applicable(tgt) # type: ignore[misc] ) return result
def calculate_specs( options_bootstrapper: OptionsBootstrapper, options: Options, session: SchedulerSession, ) -> Specs: """Determine the specs for a given Pants run.""" global_options = options.for_global_scope() unmatched_cli_globs = global_options.unmatched_cli_globs.to_glob_match_error_behavior( ) convert_dir_literal_to_address_literal = ( global_options.use_deprecated_directory_cli_args_semantics) if global_options.is_default( "use_deprecated_directory_cli_args_semantics"): warn_or_error( "2.14.0.dev0", "`use_deprecated_directory_cli_args_semantics` defaulting to True", softwrap(f""" Currently, a directory argument like `{bin_name()} test dir` is shorthand for the target `dir:dir`, i.e. the target that leaves off `name=`. In Pants 2.14, by default, a directory argument will instead match all targets/files in the directory. To opt into the new and more intuitive semantics early, set `use_deprecated_directory_cli_args_semantics = false` in the `[GLOBAL]` section in `pants.toml`. Otherwise, set to `true` to silence this warning. """), ) specs = SpecsParser().parse_specs( options.specs, description_of_origin="CLI arguments", unmatched_glob_behavior=unmatched_cli_globs, convert_dir_literal_to_address_literal= convert_dir_literal_to_address_literal, ) changed_options = ChangedOptions.from_options(options.for_scope("changed")) logger.debug("specs are: %s", specs) logger.debug("changed_options are: %s", changed_options) if specs and changed_options.provided: changed_name = "--changed-since" if changed_options.since else "--changed-diffspec" specs_description = specs.arguments_provided_description() assert specs_description is not None raise InvalidSpecConstraint( f"You used `{changed_name}` at the same time as using {specs_description}. You can " f"only use `{changed_name}` or use normal arguments.") if not changed_options.provided: return specs (git_binary, ) = session.product_request(GitBinary, [Params(GitBinaryRequest())]) (maybe_git_worktree, ) = session.product_request( MaybeGitWorktree, [Params(GitWorktreeRequest(), git_binary)]) if not maybe_git_worktree.git_worktree: raise InvalidSpecConstraint( "The `--changed-*` options are only available if Git is used for the repository." ) changed_files = tuple( changed_options.changed_files(maybe_git_worktree.git_worktree)) file_literal_specs = tuple(FileLiteralSpec(f) for f in changed_files) changed_request = ChangedRequest(changed_files, changed_options.dependees) (changed_addresses, ) = session.product_request( ChangedAddresses, [Params(changed_request, options_bootstrapper)]) logger.debug("changed addresses: %s", changed_addresses) address_literal_specs = [] for address in cast(ChangedAddresses, changed_addresses): address_input = AddressInput.parse( address.spec, description_of_origin="`--changed-since`") address_literal_specs.append( AddressLiteralSpec( path_component=address_input.path_component, target_component=address_input.target_component, generated_component=address_input.generated_component, parameters=address_input.parameters, )) return Specs( includes=RawSpecs( # We need both address_literals and file_literals to cover all our edge cases, including # target-aware vs. target-less goals, e.g. `list` vs `count-loc`. address_literals=tuple(address_literal_specs), file_literals=file_literal_specs, unmatched_glob_behavior=unmatched_cli_globs, filter_by_global_options=True, from_change_detection=True, description_of_origin="`--changed-since`", ), ignores=RawSpecs(description_of_origin="`--changed-since`"), )
async def generate_chroot(request: SetupPyChrootRequest) -> SetupPyChroot: exported_target = request.exported_target owned_deps = await Get(OwnedDependencies, DependencyOwner(exported_target)) transitive_targets = await Get(TransitiveTargets, Addresses([exported_target.target.address])) # files() targets aren't owned by a single exported target - they aren't code, so # we allow them to be in multiple dists. This is helpful for, e.g., embedding # a standard license file in a dist. files_targets = (tgt for tgt in transitive_targets.closure if tgt.has_field(FilesSources)) targets = Targets( itertools.chain((od.target for od in owned_deps), files_targets)) sources = await Get(SetupPySources, SetupPySourcesRequest(targets, py2=request.py2)) requirements = await Get(ExportedTargetRequirements, DependencyOwner(exported_target)) # Nest the sources under the src/ prefix. src_digest = await Get(Digest, AddPrefix(sources.digest, CHROOT_SOURCE_ROOT)) target = exported_target.target provides = exported_target.provides # Generate the kwargs to the setup() call. setup_kwargs = provides.setup_py_keywords.copy() setup_kwargs.update({ "package_dir": { "": CHROOT_SOURCE_ROOT }, "packages": sources.packages, "namespace_packages": sources.namespace_packages, "package_data": dict(sources.package_data), "install_requires": tuple(requirements), }) key_to_binary_spec = provides.binaries keys = list(key_to_binary_spec.keys()) addresses = await MultiGet( Get( Address, AddressInput, AddressInput.parse(key_to_binary_spec[key], relative_to=target.address.spec_path), ) for key in keys) binaries = await Get(Targets, Addresses(addresses)) for key, binary in zip(keys, binaries): binary_entry_point = binary.get(PythonEntryPoint).value if not binary_entry_point: raise InvalidEntryPoint( f"The binary {key} exported by {target.address} is not a valid entry point." ) entry_points = setup_kwargs["entry_points"] = setup_kwargs.get( "entry_points", {}) console_scripts = entry_points["console_scripts"] = entry_points.get( "console_scripts", []) console_scripts.append(f"{key}={binary_entry_point}") # Generate the setup script. setup_py_content = SETUP_BOILERPLATE.format( target_address_spec=target.address.spec, setup_kwargs_str=distutils_repr(setup_kwargs), ).encode() extra_files_digest = await Get( Digest, CreateDigest([ FileContent("setup.py", setup_py_content), FileContent( "MANIFEST.in", "include *.py".encode()), # Make sure setup.py is included. ]), ) chroot_digest = await Get(Digest, MergeDigests((src_digest, extra_files_digest))) return SetupPyChroot(chroot_digest, json.dumps(setup_kwargs, sort_keys=True))
async def resolve_dependencies( request: DependenciesRequest, target_types_to_generate_requests: TargetTypesToGenerateTargetsRequests, union_membership: UnionMembership, global_options: GlobalOptions, ) -> Addresses: wrapped_tgt, explicitly_provided = await MultiGet( Get(WrappedTarget, Address, request.field.address), Get(ExplicitlyProvidedDependencies, DependenciesRequest, request), ) tgt = wrapped_tgt.target # Inject any dependencies (based on `Dependencies` field rather than `Sources` field). inject_request_types = union_membership.get(InjectDependenciesRequest) injected = await MultiGet( Get(InjectedDependencies, InjectDependenciesRequest, inject_request_type(request.field)) for inject_request_type in inject_request_types if isinstance(request.field, inject_request_type.inject_for)) # Infer any dependencies (based on `Sources` field). inference_request_types = union_membership.get(InferDependenciesRequest) inferred: Tuple[InferredDependencies, ...] = () if inference_request_types: sources_field = tgt.get(Sources) relevant_inference_request_types = [ inference_request_type for inference_request_type in inference_request_types if isinstance(sources_field, inference_request_type.infer_from) ] inferred = await MultiGet( Get( InferredDependencies, InferDependenciesRequest, inference_request_type(sources_field), ) for inference_request_type in relevant_inference_request_types) # If it's a target generator, inject dependencies on all of its generated targets. generated_addresses: tuple[Address, ...] = () if target_types_to_generate_requests.is_generator( tgt) and not tgt.address.is_generated_target: generate_request = target_types_to_generate_requests[type(tgt)] generated_targets = await Get(GeneratedTargets, GenerateTargetsRequest, generate_request(tgt)) generated_addresses = tuple(generated_targets.keys()) # If the target has `SpecialCasedDependencies`, such as the `archive` target having # `files` and `packages` fields, then we possibly include those too. We don't want to always # include those dependencies because they should often be excluded from the result due to # being handled elsewhere in the calling code. special_cased: Tuple[Address, ...] = () if request.include_special_cased_deps: # Unlike normal, we don't use `tgt.get()` because there may be >1 subclass of # SpecialCasedDependencies. special_cased_fields = tuple( field for field in tgt.field_values.values() if isinstance(field, SpecialCasedDependencies)) # We can't use the normal `Get(Addresses, UnparsedAddressInputs)` due to a graph cycle. special_cased = await MultiGet( Get( Address, AddressInput, AddressInput.parse( addr, relative_to=tgt.address.spec_path, subproject_roots=global_options.options.subproject_roots, ), ) for special_cased_field in special_cased_fields for addr in special_cased_field.to_unparsed_address_inputs().values ) result = { addr for addr in ( *generated_addresses, *explicitly_provided.includes, *itertools.chain.from_iterable(injected), *itertools.chain.from_iterable(inferred), *special_cased, ) if addr not in explicitly_provided.ignores } return Addresses(sorted(result))
async def determine_main_pkg_for_go_binary( request: GoBinaryMainPackageRequest, ) -> GoBinaryMainPackage: addr = request.field.address if request.field.value: description_of_origin = ( f"the `{request.field.alias}` field from the target {request.field.address}" ) specified_address = await Get( Address, AddressInput, AddressInput.parse( request.field.value, relative_to=addr.spec_path, description_of_origin=description_of_origin, ), ) wrapped_specified_tgt = await Get( WrappedTarget, WrappedTargetRequest(specified_address, description_of_origin=description_of_origin), ) if not wrapped_specified_tgt.target.has_field(GoPackageSourcesField): raise InvalidFieldException( f"The {repr(GoBinaryMainPackageField.alias)} field in target {addr} must point to " "a `go_package` target, but was the address for a " f"`{wrapped_specified_tgt.target.alias}` target.\n\n" "Hint: you should normally not specify this field so that Pants will find the " "`go_package` target for you." ) return GoBinaryMainPackage(wrapped_specified_tgt.target.address) candidate_targets = await Get( Targets, RawSpecs( dir_globs=(DirGlobSpec(addr.spec_path),), description_of_origin="the `go_binary` dependency inference rule", ), ) relevant_pkg_targets = [ tgt for tgt in candidate_targets if tgt.has_field(GoPackageSourcesField) and tgt.residence_dir == addr.spec_path ] if len(relevant_pkg_targets) == 1: return GoBinaryMainPackage(relevant_pkg_targets[0].address) if not relevant_pkg_targets: raise ResolveError( f"The target {addr} requires that there is a `go_package` " f"target defined in its directory {addr.spec_path}, but none were found.\n\n" "To fix, add a target like `go_package()` or `go_package(name='pkg')` to the BUILD " f"file in {addr.spec_path}." ) raise ResolveError( f"There are multiple `go_package` targets for the same directory of the " f"target {addr}: {addr.spec_path}. It is ambiguous what to use as the `main` " "package.\n\n" f"To fix, please either set the `main` field for `{addr} or remove these " "`go_package` targets so that only one remains: " f"{sorted(tgt.address.spec for tgt in relevant_pkg_targets)}" )