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