Exemple #1
0
async def resolve_target(
    request: WrappedTargetRequest,
    target_types_to_generate_requests: TargetTypesToGenerateTargetsRequests,
) -> WrappedTarget:
    address = request.address
    base_address = address.maybe_convert_to_target_generator()
    parametrizations = await Get(
        _TargetParametrizations,
        _TargetParametrizationsRequest(
            base_address, description_of_origin=request.description_of_origin),
    )
    if address.is_generated_target:
        # TODO: This is an accommodation to allow using file/generator Addresses for
        # non-generator atom targets. See https://github.com/pantsbuild/pants/issues/14419.
        original_target = parametrizations.get(base_address)
        if original_target and not target_types_to_generate_requests.is_generator(
                original_target):
            return WrappedTarget(original_target)
    target = parametrizations.get(address)
    if target is None:
        raise ValueError(
            f"The address `{address}` was not generated by the target `{base_address}`, which "
            "only generated these addresses:\n\n"
            f"{bullet_list(str(t.address) for t in parametrizations.all)}\n\n"
            "Did you mean to use one of those addresses?")
    return WrappedTarget(target)
Exemple #2
0
async def resolve_target(
    hydrated_struct: HydratedStruct,
    registered_target_types: RegisteredTargetTypes,
    union_membership: UnionMembership,
) -> WrappedTarget:
    kwargs = hydrated_struct.value.kwargs().copy()
    type_alias = kwargs.pop("type_alias")

    # We special case `address` and the field `name`. The `Target` constructor requires an
    # `Address`, so we use the value pre-calculated via `build_files.py`'s `hydrate_struct` rule.
    # We throw away the field `name` because it can be accessed via `tgt.address.target_name`, so
    # there is no (known) reason to preserve the field.
    address = cast(Address, kwargs.pop("address"))
    kwargs.pop("name", None)

    target_type = registered_target_types.aliases_to_types.get(type_alias, None)
    if target_type is None:
        raise UnrecognizedTargetTypeException(type_alias, registered_target_types, address=address)

    # Not every target type has the Dependencies field registered, but the StructWithDependencies
    # code means that `kwargs` will always have an entry. We must remove `dependencies` from
    # `kwargs` for target types without the value, otherwise we'll get an "unrecognized field"
    # error. But, we also need to be careful to error if the user did explicitly specify
    # `dependencies` in the BUILD file.
    if kwargs["dependencies"] is None and not target_type.class_has_field(
        Dependencies, union_membership=union_membership
    ):
        kwargs.pop("dependencies")

    return WrappedTarget(target_type(kwargs, address=address))
Exemple #3
0
def single_target_run(
    rule_runner: RuleRunner,
    address: Address,
    *,
    program_text: bytes,
) -> Run:
    workspace = Workspace(rule_runner.scheduler, _enforce_effects=False)

    class TestRunFieldSet(RunFieldSet):
        required_fields = ()

    class TestBinaryTarget(Target):
        alias = "binary"
        core_fields = ()

    target = TestBinaryTarget({}, address)
    field_set = TestRunFieldSet.create(target)

    with mock_console(rule_runner.options_bootstrapper) as (console, _):
        res = run_rule_with_mocks(
            run,
            rule_args=[
                create_goal_subsystem(RunSubsystem, args=[], cleanup=True),
                create_subsystem(GlobalOptions,
                                 pants_workdir=rule_runner.pants_workdir,
                                 process_cleanup=True),
                workspace,
                BuildRoot(),
                rule_runner.environment,
            ],
            mock_gets=[
                MockGet(
                    output_type=TargetRootsToFieldSets,
                    input_type=TargetRootsToFieldSetsRequest,
                    mock=lambda _: TargetRootsToFieldSets(
                        {target: [field_set]}),
                ),
                MockGet(
                    output_type=WrappedTarget,
                    input_type=WrappedTargetRequest,
                    mock=lambda _: WrappedTarget(target),
                ),
                MockGet(
                    output_type=RunRequest,
                    input_type=TestRunFieldSet,
                    mock=lambda _: create_mock_run_request(
                        rule_runner, program_text),
                ),
                MockEffect(
                    output_type=InteractiveProcessResult,
                    input_type=InteractiveProcess,
                    mock=rule_runner.run_interactive_process,
                ),
            ],
        )
        return cast(Run, res)
Exemple #4
0
async def resolve_target(
    address: Address,
    registered_target_types: RegisteredTargetTypes,
    union_membership: UnionMembership,
) -> WrappedTarget:
    if address.is_file_target:
        build_target = await Get(WrappedTarget, Address, address.maybe_convert_to_build_target())
        subtarget = generate_subtarget(
            build_target.target, full_file_name=address.filename, union_membership=union_membership
        )
        return WrappedTarget(subtarget)

    target_adaptor = await Get(TargetAdaptor, Address, address)
    target_type = registered_target_types.aliases_to_types.get(target_adaptor.type_alias, None)
    if target_type is None:
        raise UnrecognizedTargetTypeException(
            target_adaptor.type_alias, registered_target_types, address=address
        )
    target = target_type(target_adaptor.kwargs, address=address, union_membership=union_membership)
    return WrappedTarget(target)
Exemple #5
0
async def resolve_target(
    address: Address,
    registered_target_types: RegisteredTargetTypes,
    union_membership: UnionMembership,
    target_types_to_generate_requests: TargetTypesToGenerateTargetsRequests,
) -> WrappedTarget:
    if not address.is_generated_target:
        target_adaptor = await Get(TargetAdaptor, Address, address)
        target_type = registered_target_types.aliases_to_types.get(
            target_adaptor.type_alias, None)
        if target_type is None:
            raise UnrecognizedTargetTypeException(target_adaptor.type_alias,
                                                  registered_target_types,
                                                  address)
        if (target_type.deprecated_alias is not None
                and target_type.deprecated_alias == target_adaptor.type_alias
                and not address.is_generated_target):
            await Get(_WarnDeprecatedTarget,
                      _WarnDeprecatedTargetRequest(target_type))
        target = target_type(target_adaptor.kwargs, address, union_membership)
        return WrappedTarget(target)

    wrapped_generator_tgt = await Get(
        WrappedTarget, Address, address.maybe_convert_to_target_generator())
    generator_tgt = wrapped_generator_tgt.target
    if not target_types_to_generate_requests.is_generator(generator_tgt):
        # TODO: Error in this case. You should not use a generator address (or file address) if
        #  the generator does not actually generate.
        return wrapped_generator_tgt

    generate_request = target_types_to_generate_requests[type(generator_tgt)]
    generated = await Get(GeneratedTargets, GenerateTargetsRequest,
                          generate_request(generator_tgt))
    if address not in generated:
        raise ValueError(
            f"The address `{address}` is not generated by the `{generator_tgt.alias}` target "
            f"`{generator_tgt.address}`, which only generates these addresses:\n\n"
            f"{bullet_list(addr.spec for addr in generated)}\n\n"
            "Did you mean to use one of those addresses?")
    return WrappedTarget(generated[address])
Exemple #6
0
async def resolve_target(
    hydrated_struct: HydratedStruct,
    registered_target_types: RegisteredTargetTypes,
    union_membership: UnionMembership,
) -> WrappedTarget:
    kwargs = hydrated_struct.value.kwargs().copy()
    type_alias = kwargs.pop("type_alias")

    # We special case `address` and the field `name`. The `Target` constructor requires an
    # `Address`, so we use the value pre-calculated via `build_files.py`'s `hydrate_struct` rule.
    # We throw away the field `name` because it can be accessed via `tgt.address.target_name`, so
    # there is no (known) reason to preserve the field.
    address = cast(Address, kwargs.pop("address"))
    kwargs.pop("name", None)

    # We convert `source` into `sources` because the Target API has no `Source` field, only
    # `Sources`.
    if "source" in kwargs and "sources" in kwargs:
        raise TargetDefinitionException(
            address, "Cannot specify both `source` and `sources` fields."
        )
    if "source" in kwargs:
        source = kwargs.pop("source")
        if not isinstance(source, str):
            raise TargetDefinitionException(
                address,
                f"The `source` field must be a string containing a path relative to the target, "
                f"but got {source} of type {type(source)}.",
            )
        kwargs["sources"] = [source]

    target_type = registered_target_types.aliases_to_types.get(type_alias, None)
    if target_type is None:
        raise TargetDefinitionException(
            address,
            f"Target type {repr(type_alias)} is not recognized. All valid target types: "
            f"{sorted(registered_target_types.aliases)}.",
        )

    # Not every target type has the Dependencies field registered, but the StructWithDependencies
    # code means that `kwargs` will always have an entry. We must remove `dependencies` from
    # `kwargs` for target types without the value, otherwise we'll get an "unrecognized field"
    # error. But, we also need to be careful to error if the user did explicitly specify
    # `dependencies` in the BUILD file.
    if kwargs["dependencies"] is None and not target_type.class_has_field(
        Dependencies, union_membership=union_membership
    ):
        kwargs.pop("dependencies")

    return WrappedTarget(target_type(kwargs, address=address))
Exemple #7
0
    def test_transitive_targets(self) -> None:
        t1 = MockTarget({}, address=Address.parse(":t1"))
        t2 = MockTarget({Dependencies.alias: [t1.address]},
                        address=Address.parse(":t2"))
        d1 = MockTarget({Dependencies.alias: [t1.address]},
                        address=Address.parse(":d1"))
        d2 = MockTarget({Dependencies.alias: [t2.address]},
                        address=Address.parse(":d2"))
        d3 = MockTarget({}, address=Address.parse(":d3"))
        root = MockTarget(
            {Dependencies.alias: [d1.address, d2.address, d3.address]},
            address=Address.parse(":root"),
        )

        self.add_to_build_file(
            "",
            dedent("""\
                target(name='t1')
                target(name='t2', dependencies=[':t1'])
                target(name='d1', dependencies=[':t1'])
                target(name='d2', dependencies=[':t2'])
                target(name='d3')
                target(name='root', dependencies=[':d1', ':d2', ':d3'])
                """),
        )

        direct_deps = self.request_single_product(
            Targets,
            Params(DependenciesRequest(root[Dependencies]),
                   create_options_bootstrapper()))
        assert direct_deps == Targets([d1, d2, d3])

        transitive_target = self.request_single_product(
            TransitiveTarget,
            Params(WrappedTarget(root), create_options_bootstrapper()))
        assert transitive_target.root == root
        assert {
            dep_transitive_target.root
            for dep_transitive_target in transitive_target.dependencies
        } == {d1, d2, d3}

        transitive_targets = self.request_single_product(
            TransitiveTargets,
            Params(Addresses([root.address, d2.address]),
                   create_options_bootstrapper()),
        )
        assert transitive_targets.roots == (root, d2)
        assert transitive_targets.closure == FrozenOrderedSet(
            [root, d2, d1, d3, t2, t1])
Exemple #8
0
    def test_transitive_targets(self) -> None:
        t1 = MockTarget({}, address=Address.parse(":t1"))
        t2 = MockTarget({Dependencies.alias: [t1.address]},
                        address=Address.parse(":t2"))
        d1 = MockTarget({Dependencies.alias: [t1.address]},
                        address=Address.parse(":d1"))
        d2 = MockTarget({Dependencies.alias: [t2.address]},
                        address=Address.parse(":d2"))
        d3 = MockTarget({}, address=Address.parse(":d3"))
        root = MockTarget(
            {Dependencies.alias: [d1.address, d2.address, d3.address]},
            address=Address.parse(":root"),
        )

        # TODO: possibly figure out how to deduplicate this when developing utilities for testing
        #  with the Target API.
        self.add_to_build_file(
            "",
            dedent("""\
                target(name='t1')
                target(name='t2', dependencies=[':t1'])
                target(name='d1', dependencies=[':t1'])
                target(name='d2', dependencies=[':t2'])
                target(name='d3')
                target(name='root', dependencies=[':d1', ':d2', ':d3'])
                """),
        )

        direct_deps = self.request_single_product(
            Targets,
            Addresses(root[Dependencies].value)  # type: ignore[arg-type]
        )
        assert direct_deps == Targets([d1, d2, d3])

        transitive_target = self.request_single_product(
            TransitiveTarget, WrappedTarget(root))
        assert transitive_target.root == root
        assert {
            dep_transitive_target.root
            for dep_transitive_target in transitive_target.dependencies
        } == {d1, d2, d3}

        transitive_targets = self.request_single_product(
            TransitiveTargets, Addresses([root.address, d2.address]))
        assert transitive_targets.roots == (root, d2)
        assert transitive_targets.closure == FrozenOrderedSet(
            [root, d2, d1, d3, t2, t1])
Exemple #9
0
def assert_build(
        rule_runner: RuleRunner,
        address: Address,
        *extra_log_lines: str,
        options: dict | None = None,
        process_assertions: Callable[[Process], None] | None = None,
        exit_code: int = 0,
        copy_sources: tuple[str, ...] = (),
        build_context_snapshot: Snapshot = EMPTY_SNAPSHOT,
        version_tags: tuple[str, ...] = (),
) -> None:
    tgt = rule_runner.get_target(address)

    def build_context_mock(
            request: DockerBuildContextRequest) -> DockerBuildContext:
        return DockerBuildContext.create(
            snapshot=build_context_snapshot,
            dockerfile_info=DockerfileInfo(
                request.address,
                digest=EMPTY_DIGEST,
                source=os.path.join(address.spec_path, "Dockerfile"),
                copy_source_paths=copy_sources,
                version_tags=version_tags,
            ),
            build_args=rule_runner.request(DockerBuildArgs,
                                           [DockerBuildArgsRequest(tgt)]),
            build_env=rule_runner.request(
                DockerBuildEnvironment, [DockerBuildEnvironmentRequest(tgt)]),
        )

    def run_process_mock(process: Process) -> FallibleProcessResult:
        if process_assertions:
            process_assertions(process)

        return FallibleProcessResult(
            exit_code=exit_code,
            stdout=b"stdout",
            stdout_digest=EMPTY_FILE_DIGEST,
            stderr=b"stderr",
            stderr_digest=EMPTY_FILE_DIGEST,
            output_digest=EMPTY_DIGEST,
            platform=Platform.current,
            metadata=ProcessResultMetadata(0, "ran_locally", 0),
        )

    if options:
        opts = options or {}
        opts.setdefault("registries", {})
        opts.setdefault("default_repository", "{name}")
        opts.setdefault("default_context_root", "")
        opts.setdefault("build_args", [])
        opts.setdefault("build_target_stage", None)
        opts.setdefault("build_verbose", False)
        opts.setdefault("env_vars", [])

        docker_options = create_subsystem(
            DockerOptions,
            **opts,
        )
    else:
        docker_options = rule_runner.request(DockerOptions, [])

    global_options = rule_runner.request(GlobalOptions, [])

    result = run_rule_with_mocks(
        build_docker_image,
        rule_args=[
            DockerFieldSet.create(tgt),
            docker_options,
            global_options,
            DockerBinary("/dummy/docker"),
            ProcessCleanupOption(True),
        ],
        mock_gets=[
            MockGet(
                output_type=DockerBuildContext,
                input_type=DockerBuildContextRequest,
                mock=build_context_mock,
            ),
            MockGet(
                output_type=WrappedTarget,
                input_type=WrappedTargetRequest,
                mock=lambda _: WrappedTarget(tgt),
            ),
            MockGet(
                output_type=FallibleProcessResult,
                input_type=Process,
                mock=run_process_mock,
            ),
        ],
    )

    assert result.digest == EMPTY_DIGEST
    assert len(result.artifacts) == 1
    assert result.artifacts[0].relpath is None

    for log_line in extra_log_lines:
        assert log_line in result.artifacts[0].extra_log_lines