def build_image()

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


def build_image(args: argparse.Namespace, argv: List[str]) -> SubvolumeOnDisk:
    # We want check the umask since it can affect the result of the
    # `os.access` check for `image.install*` items.  That said, having a
    # umask that denies execute permission to "user" is likely to break this
    # code earlier, since new directories wouldn't be traversible.  At least
    # this check gives a nice error message.
    cur_umask = os.umask(0)
    os.umask(cur_umask)
    assert (
        cur_umask & stat.S_IXUSR == 0
    ), f"Refusing to run with pathological umask 0o{cur_umask:o}"

    subvol = Subvol(args.subvolumes_dir / args.subvolume_rel_path)

    flavor_config = args.flavor_config

    if not flavor_config:
        assert (
            args.parent_layer
        ), "Parent layer must be given if no flavor config is given"
        flavor_config = get_parent_layer_flavor_config(args.parent_layer)

    build_appliance = None
    if flavor_config and flavor_config.build_appliance:
        build_appliance_layer_path = args.targets_and_outputs[
            flavor_config.build_appliance
        ]
        build_appliance = find_built_subvol(build_appliance_layer_path)

    # Avoid running the compiler inside of the BA if:
    # 1. The BA isn't set (ie. DO_NOT_USE_BUILD_APPLIANCE). Future: create a
    #    separate lightweight compiler binary for this case.
    # 2. We're already nested inside the BA container.
    # 3. We're compiling a genrule layer. Future: support serving rpm snapshot
    #    in the BA container to remove this restriction.
    if (
        build_appliance
        and not args.is_nested
        and not args.internal_only_is_genrule_layer
    ):
        invoke_compiler_inside_build_appliance(
            build_appliance=build_appliance,
            snapshot_dir=not_none(Path(flavor_config.rpm_repo_snapshot)),
            args=args,
            argv=argv,
        )
    else:
        layer_opts = LayerOpts(
            layer_target=args.child_layer_target,
            build_appliance=build_appliance,
            rpm_installer=YumDnf(flavor_config.rpm_installer),
            rpm_repo_snapshot=Path(flavor_config.rpm_repo_snapshot),
            artifacts_may_require_repo=args.artifacts_may_require_repo,
            target_to_path=args.targets_and_outputs,
            subvolumes_dir=args.subvolumes_dir,
            version_set_override=args.version_set_override,
            debug=args.debug,
            allowed_host_mount_targets=frozenset(
                args.allowed_host_mount_target
            ),
            flavor=flavor_config.name,
            # This value should never be inherited from the parent layer
            # as it is generally used to create a new build appliance flavor
            # by force overriding an existing flavor.
            unsafe_bypass_flavor_check=flavor_config.unsafe_bypass_flavor_check,
        )

        # This stack allows build items to hold temporary state on disk.
        with ExitStack() as exit_stack:
            compile_items_to_subvol(
                exit_stack=exit_stack,
                subvol=subvol,
                layer_opts=layer_opts,
                iter_items=gen_items_for_features(
                    exit_stack=exit_stack,
                    features_or_paths=[
                        normalize_buck_path(output)
                        for output in args.child_feature_json
                    ],
                    layer_opts=layer_opts,
                ),
                # use threads to speed up normal builds, but not while profiling
                # because multithreaded profiling is terrible
                use_threads=not args.profile_dir,
            )
            # Build artifacts should never change. Run this BEFORE the
            # exit_stack cleanup to enforce that the cleanup does not
            # touch the image.
            subvol.set_readonly(True)

    try:
        return SubvolumeOnDisk.from_subvolume_path(
            # Converting to a path here does not seem too risky since this
            # class shouldn't have a reason to follow symlinks in the subvol.
            subvol.path(),
            args.subvolumes_dir,
            build_appliance.path() if build_appliance else None,
        )
    # The complexity of covering this is high, but the only thing that can
    # go wrong is a typo in the f-string.
    except Exception as ex:  # pragma: no cover
        raise RuntimeError(f"Serializing subvolume {subvol.path()}") from ex