Esempio n. 1
0
def test_raw_specs_without_file_owners_do_not_exist(
        rule_runner: RuleRunner) -> None:
    rule_runner.write_files({
        "real/f.txt": "",
        "real/BUILD": "target(sources=['f.txt'])",
        "empty/BUILD": "# empty"
    })

    def assert_resolve_error(spec: Spec, *, expected: str) -> None:
        with engine_error(contains=expected):
            resolve_raw_specs_without_file_owners(rule_runner, [spec])

    def assert_does_not_error(spec: Spec,
                              *,
                              ignore_nonexistent: bool = False) -> None:
        assert not resolve_raw_specs_without_file_owners(
            rule_runner, [spec], ignore_nonexistent=ignore_nonexistent)

    # Literal addresses require for the target to be resolved.
    assert_resolve_error(AddressLiteralSpec("fake", "tgt"),
                         expected="'fake' does not exist on disk")
    assert_resolve_error(
        AddressLiteralSpec("fake/f.txt", "tgt"),
        expected="'fake/f.txt' does not exist on disk",
    )
    did_you_mean = ResolveError.did_you_mean(bad_name="fake_tgt",
                                             known_names=["real"],
                                             namespace="real")
    assert_resolve_error(AddressLiteralSpec("real", "fake_tgt"),
                         expected=str(did_you_mean))
    assert_resolve_error(AddressLiteralSpec("real/f.txt", "fake_tgt"),
                         expected=str(did_you_mean))

    assert_resolve_error(DirGlobSpec("fake"),
                         expected='Unmatched glob from tests: "fake/*"')
    assert_does_not_error(DirGlobSpec("empty"))
    assert_does_not_error(DirGlobSpec("fake"), ignore_nonexistent=True)

    assert_resolve_error(DirLiteralSpec("fake"),
                         expected='Unmatched glob from tests: "fake/*"')
    assert_does_not_error(DirLiteralSpec("empty"))
    assert_does_not_error(DirLiteralSpec("fake"), ignore_nonexistent=True)

    assert_resolve_error(RecursiveGlobSpec("fake"),
                         expected='Unmatched glob from tests: "fake/**"')
    assert_does_not_error(RecursiveGlobSpec("empty"))
    assert_does_not_error(RecursiveGlobSpec("fake"), ignore_nonexistent=True)

    assert_resolve_error(AncestorGlobSpec("fake"),
                         expected='Unmatched glob from tests: "fake/*"')
    assert_does_not_error(AncestorGlobSpec("empty"))
    assert_does_not_error(AncestorGlobSpec("fake"), ignore_nonexistent=True)
Esempio n. 2
0
async def infer_terraform_module_dependencies(
    request: InferTerraformModuleDependenciesRequest,
) -> InferredDependencies:
    hydrated_sources = await Get(HydratedSources, HydrateSourcesRequest(request.sources_field))

    paths = OrderedSet(
        filename for filename in hydrated_sources.snapshot.files if filename.endswith(".tf")
    )
    result = await Get(
        ProcessResult,
        ParseTerraformModuleSources(
            sources_digest=hydrated_sources.snapshot.digest,
            paths=tuple(paths),
        ),
    )
    candidate_spec_paths = [line for line in result.stdout.decode("utf-8").split("\n") if line]

    # For each path, see if there is a `terraform_module` target at the specified spec_path.
    candidate_targets = await Get(
        Targets,
        RawSpecs(
            dir_globs=tuple(DirGlobSpec(path) for path in candidate_spec_paths),
            unmatched_glob_behavior=GlobMatchErrorBehavior.ignore,
            description_of_origin="the `terraform_module` dependency inference rule",
        ),
    )
    # TODO: Need to either implement the standard ambiguous dependency logic or ban >1 terraform_module
    # per directory.
    terraform_module_addresses = [
        tgt.address for tgt in candidate_targets if tgt.has_field(TerraformModuleSourcesField)
    ]
    return InferredDependencies(terraform_module_addresses)
Esempio n. 3
0
def test_raw_specs_without_file_owners_deduplication(
        rule_runner: RuleRunner) -> None:
    """When multiple specs cover the same address, we should deduplicate to one single Address."""
    rule_runner.write_files({
        "demo/f.txt":
        "",
        "demo/BUILD":
        dedent("""\
                file_generator(sources=['f.txt'])
                nonfile_generator(name="nonfile")
                """),
    })
    specs = [
        AddressLiteralSpec("demo"),
        DirLiteralSpec("demo"),
        DirGlobSpec("demo"),
        RecursiveGlobSpec("demo"),
        AncestorGlobSpec("demo"),
        AddressLiteralSpec("demo",
                           target_component="nonfile",
                           generated_component="gen"),
        AddressLiteralSpec("demo/f.txt"),
    ]
    assert resolve_raw_specs_without_file_owners(rule_runner, specs) == [
        Address("demo"),
        Address("demo", target_name="nonfile"),
        Address("demo", target_name="nonfile", generated_name="gen"),
        Address("demo", relative_file_path="f.txt"),
    ]
Esempio n. 4
0
    def parse_spec(self, spec: str) -> tuple[Spec, bool]:
        """Parse the given spec string and also return `true` if it's an ignore.

        :raises: CmdLineSpecParser.BadSpecError if the address selector could not be parsed.
        """
        is_ignore = False
        if spec.startswith("-"):
            is_ignore = True
            spec = spec[1:]

        (
            (
                path_component,
                target_component,
                generated_component,
                parameters,
            ),
            wildcard,
        ) = native_engine.address_spec_parse(spec)

        if wildcard == "::":
            return RecursiveGlobSpec(
                directory=self._normalize_spec_path(path_component)), is_ignore
        if wildcard == ":":
            return DirGlobSpec(
                directory=self._normalize_spec_path(path_component)), is_ignore
        if target_component or generated_component or parameters:
            return (
                AddressLiteralSpec(
                    path_component=self._normalize_spec_path(path_component),
                    target_component=target_component,
                    generated_component=generated_component,
                    parameters=FrozenDict(sorted(parameters)),
                ),
                is_ignore,
            )
        if "*" in path_component:
            return FileGlobSpec(spec), is_ignore
        if PurePath(spec).suffix:
            return FileLiteralSpec(self._normalize_spec_path(spec)), is_ignore
        spec_path = self._normalize_spec_path(spec)
        if spec_path == ".":
            return DirLiteralSpec(""), is_ignore
        # Some paths that look like dirs can actually be files without extensions.
        if Path(self._root_dir, spec_path).is_file():
            return FileLiteralSpec(spec_path), is_ignore
        return DirLiteralSpec(spec_path), is_ignore
Esempio n. 5
0
def test_raw_specs_without_file_owners_filter_by_tag(
        rule_runner: RuleRunner) -> None:
    rule_runner.set_options(["--tag=+integration"])
    all_integration_tgts = [
        Address("demo", target_name="b_f"),
        Address("demo", target_name="b_nf"),
        Address("demo", target_name="b_nf", generated_name="gen"),
        Address("demo", target_name="b_f", relative_file_path="f.txt"),
    ]
    rule_runner.write_files({
        "demo/f.txt":
        "",
        "demo/BUILD":
        dedent("""\
                file_generator(name="a_f", sources=["f.txt"])
                file_generator(name="b_f", sources=["f.txt"], tags=["integration"])
                file_generator(name="c_f", sources=["f.txt"], tags=["ignore"])

                nonfile_generator(name="a_nf")
                nonfile_generator(name="b_nf", tags=["integration"])
                nonfile_generator(name="c_nf", tags=["ignore"])
                """),
    })
    assert (resolve_raw_specs_without_file_owners(
        rule_runner, [DirGlobSpec("demo")]) == all_integration_tgts)

    # The same filtering should work when given literal addresses, including generated targets and
    # file addresses.
    literals_result = resolve_raw_specs_without_file_owners(
        rule_runner,
        [
            AddressLiteralSpec("demo", "a_f"),
            AddressLiteralSpec("demo", "b_f"),
            AddressLiteralSpec("demo", "c_f"),
            AddressLiteralSpec("demo", "a_nf"),
            AddressLiteralSpec("demo", "b_nf"),
            AddressLiteralSpec("demo", "c_nf"),
            AddressLiteralSpec("demo/f.txt", "a_f"),
            AddressLiteralSpec("demo/f.txt", "b_f"),
            AddressLiteralSpec("demo", "a_nf", "gen"),
            AddressLiteralSpec("demo", "b_nf", "gen"),
            AddressLiteralSpec("demo", "c_nf", "gen"),
        ],
    )
    assert literals_result == all_integration_tgts
Esempio n. 6
0
def test_raw_specs_without_file_owners_filter_by_exclude_pattern(
        rule_runner: RuleRunner) -> None:
    rule_runner.set_options(["--exclude-target-regexp=exclude_me.*"])
    rule_runner.write_files({
        "demo/f.txt":
        "",
        "demo/BUILD":
        dedent("""\
                file_generator(name="exclude_me_f", sources=["f.txt"])
                file_generator(name="not_me_f", sources=["f.txt"])

                nonfile_generator(name="exclude_me_nf")
                nonfile_generator(name="not_me_nf")
                """),
    })
    not_me_tgts = [
        Address("demo", target_name="not_me_f"),
        Address("demo", target_name="not_me_nf"),
        Address("demo", target_name="not_me_nf", generated_name="gen"),
        Address("demo", target_name="not_me_f", relative_file_path="f.txt"),
    ]

    assert resolve_raw_specs_without_file_owners(
        rule_runner, [DirGlobSpec("demo")]) == not_me_tgts

    # The same filtering should work when given literal addresses, including generated targets and
    # file addresses.
    literals_result = resolve_raw_specs_without_file_owners(
        rule_runner,
        [
            AddressLiteralSpec("demo", "exclude_me_f"),
            AddressLiteralSpec("demo", "exclude_me_nf"),
            AddressLiteralSpec("demo", "not_me_f"),
            AddressLiteralSpec("demo", "not_me_nf"),
            AddressLiteralSpec("demo", "exclude_me_nf", "gen"),
            AddressLiteralSpec("demo", "not_me_nf", "gen"),
            AddressLiteralSpec("demo/f.txt", "exclude_me_f"),
            AddressLiteralSpec("demo/f.txt", "not_me_f"),
        ],
    )
    assert literals_result == not_me_tgts
Esempio n. 7
0
def test_raw_specs_without_file_owners_literals_vs_globs(
        rule_runner: RuleRunner) -> None:
    rule_runner.write_files({
        "demo/BUILD":
        dedent("""\
                file_generator(sources=['**/*.txt'])
                nonfile_generator(name="nonfile")
                """),
        "demo/f1.txt":
        "",
        "demo/f2.txt":
        "",
        "demo/f[3].txt":
        "",
        "demo/subdir/f.txt":
        "",
        "demo/subdir/f.another_ext":
        "",
        "demo/subdir/BUILD":
        "target(name='another_ext', sources=['f.another_ext'])",
        "another_dir/BUILD":
        "target(sources=[])",
    })

    def assert_resolved(spec: Spec, expected: set[Address]) -> None:
        result = resolve_raw_specs_without_file_owners(rule_runner, [spec])
        assert set(result) == expected

    # Literals should be "one-in, one-out".
    assert_resolved(AddressLiteralSpec("demo"), {Address("demo")})
    assert_resolved(AddressLiteralSpec("demo/f1.txt"),
                    {Address("demo", relative_file_path="f1.txt")})
    assert_resolved(
        AddressLiteralSpec("demo",
                           target_component="nonfile",
                           generated_component="gen"),
        {Address("demo", target_name="nonfile", generated_name="gen")},
    )
    assert_resolved(
        AddressLiteralSpec("demo/subdir", target_component="another_ext"),
        {Address("demo/subdir", target_name="another_ext")},
    )

    demo_dir_generated_targets = {
        Address("demo", relative_file_path="f1.txt"),
        Address("demo", relative_file_path="f2.txt"),
        Address("demo", relative_file_path="f[[]3].txt"),
        Address("demo", target_name="nonfile", generated_name="gen"),
    }
    demo_subdir_generated_targets = {
        Address("demo", relative_file_path="subdir/f.txt"),
        Address("demo/subdir", target_name="another_ext"),
    }

    # `DirGlobSpec` matches all targets that "reside" in the directory, either because explicitly
    # declared there or generated into that dir.
    assert_resolved(
        # Note that this does not include `demo/subdir/f2.ext:../demo`, even though its target
        # generator matches.
        DirGlobSpec("demo"),
        {
            Address("demo"),
            Address("demo", target_name="nonfile"), *demo_dir_generated_targets
        },
    )
    assert_resolved(
        # Should include all generated targets that reside in `demo/subdir`, even though their
        # target generator is in an ancestor.
        DirGlobSpec("demo/subdir"),
        demo_subdir_generated_targets,
    )

    # `DirLiteralSpec` matches all targets that "reside" in the directory, but it filters out
    # target generators.
    assert_resolved(DirLiteralSpec("demo"), demo_dir_generated_targets)
    assert_resolved(DirLiteralSpec("demo/subdir"),
                    demo_subdir_generated_targets)

    all_tgts_in_demo = {
        Address("demo"),
        Address("demo", target_name="nonfile"),
        *demo_dir_generated_targets,
        *demo_subdir_generated_targets,
    }
    assert_resolved(RecursiveGlobSpec("demo"), all_tgts_in_demo)

    assert_resolved(AncestorGlobSpec("demo/subdir"), all_tgts_in_demo)
    assert_resolved(
        AncestorGlobSpec("demo"),
        {
            Address("demo"),
            Address("demo", target_name="nonfile"),
            Address("demo", target_name="nonfile", generated_name="gen"),
            Address("demo", relative_file_path="f1.txt"),
            Address("demo", relative_file_path="f2.txt"),
            Address("demo", relative_file_path="f[[]3].txt"),
        },
    )
Esempio n. 8
0
def dir_glob(directory: str) -> DirGlobSpec:
    return DirGlobSpec(directory)
Esempio n. 9
0
def test_dir_glob() -> None:
    spec = DirGlobSpec("dir/subdir")
    assert spec.to_glob() == "dir/subdir/*"
    assert spec.matches_target_residence_dir("") is False
    assert spec.matches_target_residence_dir("dir") is False
    assert spec.matches_target_residence_dir("dir/subdir") is True
    assert spec.matches_target_residence_dir("dir/subdir/nested") is False
    assert spec.matches_target_residence_dir("another/subdir") is False
    assert_build_file_globs(
        RawSpecsWithoutFileOwners(dir_globs=(spec,), description_of_origin="tests"),
        expected_build_globs={"BUILD", "dir/BUILD", "dir/subdir/BUILD"},
        expected_validation_globs={"dir/subdir/*"},
    )

    spec = DirGlobSpec("")
    assert spec.to_glob() == "*"
    assert spec.matches_target_residence_dir("") is True
    assert spec.matches_target_residence_dir("dir") is False
    assert_build_file_globs(
        RawSpecsWithoutFileOwners(dir_globs=(spec,), description_of_origin="tests"),
        expected_build_globs={"BUILD"},
        expected_validation_globs={"*"},
    )
Esempio n. 10
0
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)}"
    )