def replace_targets_by_paths()

in antlir/compiler/items_for_features.py [0:0]


def replace_targets_by_paths(x: Any, ctx: GenFeaturesContext):
    """
    Converts target_tagger.bzl sigils to buck-out paths or Subvol objects.

    JSON-serialized image features store single-item dicts of the form
    {'__BUCK{_LAYER,}_TARGET': '//target:path'} whenever the compiler
    requires a path to another target.  This is because actual paths would
    break Buck caching, and would not survive repo moves.  Then, at runtime,
    the compiler receives a dictionary of target-to-path mappings as
    `--child-dependencies`, and performs the substitution in any image
    feature JSON it consumes.

    If `ctx.subvolumes_dir` is None, layer targets will not be replaced by
    their corresponding subvolumes, and will instead be left as-is.

    If `ctx.ignore_missing_paths` is True, the target will simply be
    returned if it is not found in `target_to_path`.
    """
    if type(x) is dict:
        if "__BUCK_TARGET" in x or "__BUCK_LAYER_TARGET" in x:
            assert len(x) == 1, x
            ((sigil, target),) = x.items()
            if sigil == "__BUCK_LAYER_TARGET" and ctx.subvolumes_dir is None:
                return target  # pragma: no cover
            path = ctx.target_to_path.get(target)
            if not path:
                if ctx.ignore_missing_paths:  # pragma: no cover
                    return UnknownTarget(target)
                raise RuntimeError(f"{target} not in {ctx.target_to_path}")
            return (
                path
                if sigil == "__BUCK_TARGET"
                else find_built_subvol(path, subvolumes_dir=ctx.subvolumes_dir)
            )
        return {k: replace_targets_by_paths(v, ctx) for k, v in x.items()}
    elif type(x) is list:
        return [replace_targets_by_paths(v, ctx) for v in x]
    elif type(x) in [int, float, str, bool, type(None)]:
        return x
    raise AssertionError(f"Unknown {type(x)} for {x}")  # pragma: no cover