예제 #1
0
def test_parse_dependencies_field() -> None:
    """Ensure that we correctly handle `!` ignores.

    We leave the rest of the parsing to AddressInput and Address.
    """
    result = parse_dependencies_field(
        ["a/b/c", "!a/b/c", "f.txt", "!f.txt"], spec_path="demo/subdir", subproject_roots=[],
    )
    expected_addresses = {AddressInput("a/b/c"), AddressInput("f.txt")}
    assert set(result.addresses) == expected_addresses
    assert set(result.ignored_addresses) == expected_addresses
예제 #2
0
def test_resolve_address() -> None:
    rule_runner = RuleRunner(rules=[QueryRule(Address, (AddressInput, ))])

    def assert_is_expected(address_input: AddressInput,
                           expected: Address) -> None:
        assert rule_runner.request(Address, [address_input]) == expected

    rule_runner.create_file("a/b/c.txt")
    assert_is_expected(
        AddressInput("a/b/c.txt"),
        Address("a/b", target_name=None, relative_file_path="c.txt"))
    assert_is_expected(
        AddressInput("a/b"),
        Address("a/b", target_name=None, relative_file_path=None))

    assert_is_expected(AddressInput("a/b", target_component="c"),
                       Address("a/b", target_name="c"))
    assert_is_expected(
        AddressInput("a/b/c.txt", target_component="c"),
        Address("a/b", relative_file_path="c.txt", target_name="c"),
    )

    # Top-level addresses will not have a path_component, unless they are a file address.
    rule_runner.create_file("f.txt")
    assert_is_expected(
        AddressInput("f.txt", target_component="original"),
        Address("", relative_file_path="f.txt", target_name="original"),
    )
    assert_is_expected(AddressInput("", target_component="t"),
                       Address("", target_name="t"))

    with pytest.raises(ExecutionError) as exc:
        rule_runner.request(Address, [AddressInput("a/b/fake")])
    assert "'a/b/fake' does not exist on disk" in str(exc.value)
예제 #3
0
    def test_resolve_address(self) -> None:
        def assert_is_expected(address_input: AddressInput,
                               expected: Address) -> None:
            assert self.request_single_product(Address,
                                               address_input) == expected

        self.create_file("a/b/c.txt")
        assert_is_expected(
            AddressInput("a/b/c.txt"),
            Address("a/b", target_name=None, relative_file_path="c.txt"))
        assert_is_expected(
            AddressInput("a/b"),
            Address("a/b", target_name=None, relative_file_path=None))

        assert_is_expected(AddressInput("a/b", target_component="c"),
                           Address("a/b", target_name="c"))
        assert_is_expected(
            AddressInput("a/b/c.txt", target_component="c"),
            Address("a/b", relative_file_path="c.txt", target_name="c"),
        )

        # Top-level addresses will not have a path_component, unless they are a file address.
        self.create_file("f.txt")
        assert_is_expected(
            AddressInput("f.txt", target_component="original"),
            Address("", relative_file_path="f.txt", target_name="original"),
        )
        assert_is_expected(AddressInput("", target_component="t"),
                           Address("", target_name="t"))

        with pytest.raises(ExecutionError) as exc:
            self.request_single_product(Address, AddressInput("a/b/fake"))
        assert "'a/b/fake' does not exist on disk" in str(exc.value)
예제 #4
0
async def addresses_from_address_specs(
        address_specs: AddressSpecs, global_options: GlobalOptions,
        specs_filter: AddressSpecsFilter) -> Addresses:
    matched_addresses: OrderedSet[Address] = OrderedSet()
    filtering_disabled = address_specs.filter_by_global_options is False

    # First convert all `AddressLiteralSpec`s. Some of the resulting addresses may be generated
    # addresses. This will raise an exception if any of the addresses are not valid.
    literal_addresses = await MultiGet(
        Get(
            Address,
            AddressInput(spec.path_component, spec.target_component,
                         spec.generated_component),
        ) for spec in address_specs.literals)
    literal_target_adaptors = await MultiGet(
        Get(TargetAdaptor, Address, addr.maybe_convert_to_target_generator())
        for addr in literal_addresses)
    # We convert to targets for the side effect of validating that any generated targets actually
    # belong to their target generator.
    await Get(
        UnexpandedTargets,
        Addresses(addr for addr in literal_addresses
                  if addr.is_generated_target))
    for literal_spec, addr, target_adaptor in zip(address_specs.literals,
                                                  literal_addresses,
                                                  literal_target_adaptors):
        if filtering_disabled or specs_filter.matches(addr, target_adaptor):
            matched_addresses.add(addr)

    # Then, convert all `AddressGlobSpecs`. Resolve all BUILD files covered by the specs, then
    # group by directory.
    paths = await Get(
        Paths,
        PathGlobs,
        address_specs.to_path_globs(
            build_patterns=global_options.options.build_patterns,
            build_ignore_patterns=global_options.options.build_ignore,
        ),
    )
    dirnames = {os.path.dirname(f) for f in paths.files}
    address_families = await MultiGet(
        Get(AddressFamily, AddressFamilyDir(d)) for d in dirnames)
    address_family_by_directory = {af.namespace: af for af in address_families}

    for glob_spec in address_specs.globs:
        # These may raise ResolveError, depending on the type of spec.
        addr_families_for_spec = glob_spec.matching_address_families(
            address_family_by_directory)
        addr_target_pairs_for_spec = glob_spec.matching_addresses(
            addr_families_for_spec)
        matched_addresses.update(
            addr for (addr, tgt) in addr_target_pairs_for_spec
            # TODO(#11123): handle the edge case if a generated target's `tags` != its generator's.
            if filtering_disabled or specs_filter.matches(addr, tgt))

    return Addresses(sorted(matched_addresses))
예제 #5
0
async def _determine_literal_addresses_from_raw_specs(
        literal_specs: tuple[AddressLiteralSpec, ...], *,
        description_of_origin: str) -> tuple[WrappedTarget, ...]:
    literal_addresses = await MultiGet(
        Get(
            Address,
            AddressInput(
                spec.path_component,
                spec.target_component,
                generated_component=spec.generated_component,
                parameters=spec.parameters,
                description_of_origin=description_of_origin,
            ),
        ) for spec in literal_specs)

    # We replace references to parametrized target templates with all their created targets. For
    # example:
    #  - dir:tgt -> (dir:tgt@k=v1, dir:tgt@k=v2)
    #  - dir:tgt@k=v -> (dir:tgt@k=v,another=a, dir:tgt@k=v,another=b), but not anything
    #       where @k=v is not true.
    literal_parametrizations = await MultiGet(
        Get(
            _TargetParametrizations,
            _TargetParametrizationsRequest(
                address.maybe_convert_to_target_generator(),
                description_of_origin=description_of_origin,
            ),
        ) for address in literal_addresses)

    # Note that if the address is not in the _TargetParametrizations, we must fall back to that
    # address's value. This will allow us to error that the address is invalid.
    all_candidate_addresses = itertools.chain.from_iterable(
        list(params.get_all_superset_targets(address)) or [address] for
        address, params in zip(literal_addresses, literal_parametrizations))

    # We eagerly call the `WrappedTarget` rule because it will validate that every final address
    # actually exists, such as with generated target addresses.
    return await MultiGet(
        Get(
            WrappedTarget,
            WrappedTargetRequest(addr,
                                 description_of_origin=description_of_origin))
        for addr in all_candidate_addresses)
예제 #6
0
async def addresses_from_address_specs(
    address_specs: AddressSpecs,
    build_file_options: BuildFileOptions,
    specs_filter: AddressSpecsFilter,
) -> Addresses:
    matched_addresses: OrderedSet[Address] = OrderedSet()
    filtering_disabled = address_specs.filter_by_global_options is False

    # Resolve all `AddressLiteralSpec`s. Will error on invalid addresses.
    literal_wrapped_targets = await MultiGet(
        Get(
            WrappedTarget,
            AddressInput(spec.path_component, spec.target_component,
                         spec.generated_component),
        ) for spec in address_specs.literals)
    matched_addresses.update(
        wrapped_tgt.target.address for wrapped_tgt in literal_wrapped_targets
        if filtering_disabled or specs_filter.matches(wrapped_tgt.target))
    if not address_specs.globs:
        return Addresses(matched_addresses)

    # Resolve all `AddressGlobSpecs`.
    build_file_paths = await Get(
        Paths,
        PathGlobs,
        address_specs.to_build_file_path_globs(
            build_patterns=build_file_options.patterns,
            build_ignore_patterns=build_file_options.ignores,
        ),
    )
    dirnames = {os.path.dirname(f) for f in build_file_paths.files}
    address_families = await MultiGet(
        Get(AddressFamily, AddressFamilyDir(d)) for d in dirnames)
    base_addresses = Addresses(
        itertools.chain.from_iterable(
            address_family.addresses_to_target_adaptors
            for address_family in address_families))

    target_parametrizations_list = await MultiGet(
        Get(_TargetParametrizations, Address, base_address)
        for base_address in base_addresses)
    residence_dir_to_targets = defaultdict(list)
    for target_parametrizations in target_parametrizations_list:
        for tgt in target_parametrizations.all:
            residence_dir_to_targets[tgt.residence_dir].append(tgt)

    matched_globs = set()
    for glob_spec in address_specs.globs:
        for residence_dir in residence_dir_to_targets:
            if not glob_spec.matches(residence_dir):
                continue
            matched_globs.add(glob_spec)
            matched_addresses.update(
                tgt.address for tgt in residence_dir_to_targets[residence_dir]
                if filtering_disabled or specs_filter.matches(tgt))

    unmatched_globs = [
        glob for glob in address_specs.globs
        if glob not in matched_globs and glob.error_if_no_matches
    ]
    if unmatched_globs:
        glob_description = (
            f"the address glob `{unmatched_globs[0]}`"
            if len(unmatched_globs) == 1 else
            f"these address globs: {sorted(str(glob) for glob in unmatched_globs)}"
        )
        raise ResolveError(
            f"No targets found for {glob_description}\n\n"
            f"Do targets exist in those directories? Maybe run `{bin_name()} tailor` to generate "
            f"BUILD files? See {doc_url('targets')} about targets and BUILD files."
        )

    return Addresses(sorted(matched_addresses))
예제 #7
0
async def addresses_with_origins_from_address_specs(
    address_specs: AddressSpecs, global_options: GlobalOptions, specs_filter: AddressSpecsFilter
) -> AddressesWithOrigins:
    """Given an AddressMapper and list of AddressSpecs, return matching AddressesWithOrigins.

    :raises: :class:`ResolveError` if the provided specs fail to match targets, and those spec
        types expect to have matched something.
    """
    matched_addresses: OrderedSet[Address] = OrderedSet()
    addr_to_origin: Dict[Address, AddressSpec] = {}
    filtering_disabled = address_specs.filter_by_global_options is False

    # First convert all `AddressLiteralSpec`s. Some of the resulting addresses may be file
    # addresses. This will raise an exception if any of the addresses are not valid.
    literal_addresses = await MultiGet(
        Get(Address, AddressInput(spec.path_component, spec.target_component))
        for spec in address_specs.literals
    )
    literal_target_adaptors = await MultiGet(
        Get(TargetAdaptor, Address, addr.maybe_convert_to_base_target())
        for addr in literal_addresses
    )
    # We convert to targets for the side effect of validating that any file addresses actually
    # belong to the specified base targets.
    await Get(
        UnexpandedTargets, Addresses(addr for addr in literal_addresses if not addr.is_base_target)
    )
    for literal_spec, addr, target_adaptor in zip(
        address_specs.literals, literal_addresses, literal_target_adaptors
    ):
        addr_to_origin[addr] = literal_spec
        if filtering_disabled or specs_filter.matches(addr, target_adaptor):
            matched_addresses.add(addr)

    # Then, convert all `AddressGlobSpecs`. Snapshot all BUILD files covered by the specs, then
    # group by directory.
    snapshot = await Get(
        Snapshot,
        PathGlobs,
        address_specs.to_path_globs(
            build_patterns=global_options.options.build_patterns,
            build_ignore_patterns=global_options.options.build_ignore,
        ),
    )
    dirnames = {os.path.dirname(f) for f in snapshot.files}
    address_families = await MultiGet(Get(AddressFamily, Dir(d)) for d in dirnames)
    address_family_by_directory = {af.namespace: af for af in address_families}

    for glob_spec in address_specs.globs:
        # These may raise ResolveError, depending on the type of spec.
        addr_families_for_spec = glob_spec.matching_address_families(address_family_by_directory)
        addr_target_pairs_for_spec = glob_spec.matching_addresses(addr_families_for_spec)

        for addr, _ in addr_target_pairs_for_spec:
            # A target might be covered by multiple specs, so we take the most specific one.
            addr_to_origin[addr] = AddressSpecs.more_specific(addr_to_origin.get(addr), glob_spec)

        matched_addresses.update(
            addr
            for (addr, tgt) in addr_target_pairs_for_spec
            if filtering_disabled or specs_filter.matches(addr, tgt)
        )

    return AddressesWithOrigins(
        AddressWithOrigin(address=addr, origin=addr_to_origin[addr]) for addr in matched_addresses
    )