blob: 6eadf172b4b48c0d61dc88bcb47c0c7ac6962b22 [file] [log] [blame]
# Copyright 2023 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
load("@rules_pkg//pkg:mappings.bzl", _strip_prefix = "strip_prefix")
load("@rules_pkg//pkg:providers.bzl", "PackageDirsInfo", "PackageFilegroupInfo", "PackageFilesInfo", "PackageSymlinkInfo")
load(":deploy.bzl", "deploy_local")
load(":pkg_files.bzl", "pkg_attributes", "pkg_files_impl")
visibility("//bazel/cros_pkg/...")
_KIND_FILE = "file"
_KIND_SYMLINK = "symlink"
_KIND_DIR = "dir"
def _gen_fake_ctx(ctx, src, metadata):
"""Generates a fake ctx-like object suitable for use with pkg_files_impl."""
metadata = json.decode(metadata)
attributes = pkg_attributes(
mode = metadata.get("mode", None),
user = metadata.get("user", None),
group = metadata.get("group", None),
uid = metadata.get("uid", None),
gid = metadata.get("gid", None),
)
strip_prefix = metadata.get("strip_prefix", _strip_prefix.files_only())
renames = {}
if "name" in metadata:
renames[src] = metadata["name"]
return struct(
files = struct(
srcs = src.files.to_list(),
excludes = [],
),
attr = struct(
strip_prefix = strip_prefix,
prefix = metadata["prefix"],
attributes = attributes,
renames = renames,
),
)
def _cros_pkg_filegroup_impl(ctx):
files = []
pkg_files = []
pkg_dirs = []
pkg_symlinks = []
for target in ctx.attr.include:
if PackageFilesInfo in target:
pkg_files.append((target[PackageFilesInfo], target.label))
if PackageDirsInfo in target:
pkg_dirs.append((target[PackageDirsInfo], target.label))
if PackageSymlinkInfo in target:
pkg_symlinks.append((target[PackageSymlinkInfo], target.label))
if PackageFilegroupInfo in target:
provider = target[PackageFilegroupInfo]
pkg_dirs.extend(provider.pkg_dirs)
pkg_files.extend(provider.pkg_files)
pkg_symlinks.extend(provider.pkg_symlinks)
files.append(target[DefaultInfo].files)
for symlink in json.decode(ctx.attr.symlinks):
provider = PackageSymlinkInfo(
destination = symlink["link_name"],
target = symlink["target"],
attributes = symlink["attributes"],
)
pkg_symlinks.append((provider, ctx.label))
for d in json.decode(ctx.attr.dirs):
provider = PackageDirsInfo(
dirs = d["dirs"],
attributes = d["attributes"],
)
pkg_dirs.append((provider, ctx.label))
for src, metadata in ctx.attr.srcs.items():
if PackageFilegroupInfo in src or PackageFilesInfo in src or PackageDirsInfo in src or PackageSymlinkInfo in src:
fail("{} has unexpected provider. Use include".format(
str(src.label),
))
package_files_info, default_info = pkg_files_impl(
_gen_fake_ctx(ctx, src, metadata),
)
files.append(default_info.files)
pkg_files.append((package_files_info, src.label))
return [
DefaultInfo(
files = depset(transitive = files),
),
PackageFilegroupInfo(
pkg_dirs = pkg_dirs,
pkg_files = pkg_files,
pkg_symlinks = pkg_symlinks,
),
]
_cros_pkg_filegroup = rule(
implementation = _cros_pkg_filegroup_impl,
attrs = dict(
# Input file -> json-encoded metadata for output.
srcs = attr.label_keyed_string_dict(allow_files = True),
# Json-encoded metadata.
symlinks = attr.string(mandatory = True),
# Json-encoded metadata.
dirs = attr.string(mandatory = True),
include = attr.label_list(providers = [[PackageFilesInfo], [PackageDirsInfo], [PackageSymlinkInfo], [PackageFilegroupInfo]]),
),
)
def cros_pkg_filegroup(name, srcs = [], visibility = None, include = [], **kwargs):
# Bazel doesn't support arbitrary types like Dict[string, List[Label]].
# So we're stuck with converting this to a "label_keyed_string_dict".
label_keyed_srcs = {}
symlinks = []
dirs = []
for src in srcs:
kind = getattr(src, "kind", None)
if kind == _KIND_FILE:
if len(src.srcs) > 1 and "name" in src.kwargs:
fail((
"Cannot rename a file when multiple files are " +
"specified at once (in {})"
).format(name))
for label in src.srcs:
if label in label_keyed_srcs:
fail((
"{} is specified twice in the pkg's filegroup. If " +
"this is intended, consider splitting one of them " +
"out into a rules_pkg invocation directly, and " +
"adding pkg_files = [...] to the cros_pkg_filegroup " +
"invocation."
).format(label))
label_keyed_srcs[label] = src.kwargs
elif kind == _KIND_SYMLINK:
symlinks.append(src)
elif kind == _KIND_DIR:
dirs.append(src)
else:
fail(
"Each entry in srcs should be an object generated by bazel " +
"package helper functions (eg. pkg.bin, pkg.exe, pkg.file, " +
"pkg.doc, pkg.symlink, pkg.dirs).",
)
_cros_pkg_filegroup(
name = name,
srcs = label_keyed_srcs,
symlinks = json.encode(symlinks),
dirs = json.encode(dirs),
include = include,
visibility = visibility,
**kwargs
)
deploy_local(
name = name + "_deploy_local",
filegroup = name,
visibility = visibility,
)
def custom_file_type(**defaults):
def fn(srcs, **kwargs):
kwargs = defaults | kwargs
if "dst" in kwargs:
if "name" in kwargs or "prefix" in kwargs or "strip_prefix" in kwargs:
fail("Name, prefix, and strip_prefix are incompatible with dst")
kwargs["prefix"], kwargs["name"] = kwargs["dst"].rsplit("/", 1)
kwargs["strip_prefix"] = _strip_prefix.files_only()
if "prefix" not in kwargs:
fail(
"Unable to determine destination for {}. Try using dst or prefix",
).format(repr(srcs))
return struct(
kind = "file",
srcs = srcs,
kwargs = json.encode(kwargs),
)
return fn
def symlink(
link_name,
target,
mode = "0640",
user = None,
group = None,
uid = None,
gid = None,
**kwargs):
if not link_name.startswith("/"):
fail("Link name must be an absolute path to the symlink")
return struct(
kind = "symlink",
target = target,
link_name = link_name,
attributes = json.decode(pkg_attributes(
mode = mode,
user = user,
group = group,
uid = uid,
gid = gid,
**kwargs
)),
)
def dirs(
dirs,
mode,
user = None,
group = None,
uid = None,
gid = None,
**kwargs):
return struct(
kind = "dir",
dirs = dirs,
attributes = json.decode(pkg_attributes(
mode = mode,
user = user,
group = group,
uid = uid,
gid = gid,
**kwargs
)),
)
def unimplemented(message = "Not implemented yet"):
def fn(srcs, **kwargs):
fail(message)
return fn