148 lines
5.6 KiB
Python
148 lines
5.6 KiB
Python
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
#
|
|
# This source code is licensed under both the MIT license found in the
|
|
# LICENSE-MIT file in the root directory of this source tree and the Apache
|
|
# License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
# of this source tree.
|
|
|
|
load(
|
|
"@prelude//cxx:groups.bzl",
|
|
"MATCH_ALL_LABEL",
|
|
)
|
|
load(
|
|
"@prelude//utils:graph_utils.bzl",
|
|
"breadth_first_traversal_by",
|
|
)
|
|
load(":apple_asset_catalog_types.bzl", "AppleAssetCatalogSpec")
|
|
load(":apple_core_data_types.bzl", "AppleCoreDataSpec")
|
|
load(":apple_resource_types.bzl", "AppleResourceSpec")
|
|
|
|
ResourceGroupInfo = provider(fields = [
|
|
"groups", # [Group.type]
|
|
"groups_hash", # str.type
|
|
"mappings", # {"label": str.type}
|
|
])
|
|
|
|
ResourceGraphNode = record(
|
|
label = field("label"),
|
|
# Attribute labels on the target.
|
|
labels = field([str.type], []),
|
|
# Deps of this target which might have resources transitively.
|
|
deps = field(["label"], []),
|
|
# Exported deps of this target which might have resources transitively.
|
|
exported_deps = field(["label"], []),
|
|
# Actual resource data, present when node corresponds to `apple_resource` target.
|
|
resource_spec = field([AppleResourceSpec.type, None], None),
|
|
# Actual asset catalog data, present when node corresponds to `apple_asset_catalog` target.
|
|
asset_catalog_spec = field([AppleAssetCatalogSpec.type, None], None),
|
|
# Actual core data, present when node corresponds to `core_data_model` target
|
|
core_data_spec = field([AppleCoreDataSpec.type, None], None),
|
|
)
|
|
|
|
ResourceGraphTSet = transitive_set()
|
|
|
|
ResourceGraph = provider(fields = [
|
|
"label", # "label"
|
|
"nodes", # "ResourceGraphTSet"
|
|
])
|
|
|
|
def create_resource_graph(
|
|
ctx: "context",
|
|
labels: [str.type],
|
|
deps: ["dependency"],
|
|
exported_deps: ["dependency"],
|
|
resource_spec: [AppleResourceSpec.type, None] = None,
|
|
asset_catalog_spec: [AppleAssetCatalogSpec.type, None] = None,
|
|
core_data_spec: [AppleCoreDataSpec.type, None] = None) -> ResourceGraph.type:
|
|
node = ResourceGraphNode(
|
|
label = ctx.label,
|
|
labels = labels,
|
|
deps = _with_resources_deps(deps),
|
|
exported_deps = _with_resources_deps(exported_deps),
|
|
resource_spec = resource_spec,
|
|
asset_catalog_spec = asset_catalog_spec,
|
|
core_data_spec = core_data_spec,
|
|
)
|
|
all_deps = deps + exported_deps
|
|
child_nodes = filter(None, [d.get(ResourceGraph) for d in all_deps])
|
|
return ResourceGraph(
|
|
label = ctx.label,
|
|
nodes = ctx.actions.tset(ResourceGraphTSet, value = node, children = [child_node.nodes for child_node in child_nodes]),
|
|
)
|
|
|
|
def get_resource_graph_node_map_func(graph: ResourceGraph.type):
|
|
def get_resource_graph_node_map() -> {"label": ResourceGraphNode.type}:
|
|
nodes = graph.nodes.traverse()
|
|
return {node.label: node for node in filter(None, nodes)}
|
|
|
|
return get_resource_graph_node_map
|
|
|
|
def _with_resources_deps(deps: ["dependency"]) -> ["label"]:
|
|
"""
|
|
Filters dependencies and returns only those which are relevant
|
|
to working with resources i.e. those which contains resource graph provider.
|
|
"""
|
|
graphs = filter(None, [d.get(ResourceGraph) for d in deps])
|
|
return [g.label for g in graphs]
|
|
|
|
def get_resource_group_info(ctx: "context") -> [ResourceGroupInfo.type, None]:
|
|
"""
|
|
Parses the currently analyzed context for any resource group definitions
|
|
and returns a list of all resource groups with their mappings.
|
|
"""
|
|
resource_group_map = ctx.attrs.resource_group_map
|
|
|
|
if not resource_group_map:
|
|
return None
|
|
|
|
if type(resource_group_map) == "dependency":
|
|
return resource_group_map[ResourceGroupInfo]
|
|
|
|
fail("Resource group maps must be provided as a resource_group_map rule dependency.")
|
|
|
|
def get_filtered_resources(
|
|
root: "label",
|
|
resource_graph_node_map_func,
|
|
resource_group: [str.type, None],
|
|
resource_group_mappings: [{"label": str.type}, None]) -> ([AppleResourceSpec.type], [AppleAssetCatalogSpec.type], [AppleCoreDataSpec.type]):
|
|
"""
|
|
Walks the provided DAG and collects resources matching resource groups definition.
|
|
"""
|
|
|
|
resource_graph_node_map = resource_graph_node_map_func()
|
|
|
|
def get_traversed_deps(target: "label") -> ["label"]:
|
|
node = resource_graph_node_map[target] # buildifier: disable=uninitialized
|
|
return node.exported_deps + node.deps
|
|
|
|
targets = breadth_first_traversal_by(
|
|
resource_graph_node_map,
|
|
get_traversed_deps(root),
|
|
get_traversed_deps,
|
|
)
|
|
|
|
resource_specs = []
|
|
asset_catalog_specs = []
|
|
core_data_specs = []
|
|
|
|
for target in targets:
|
|
target_resource_group = resource_group_mappings.get(target)
|
|
|
|
# Ungrouped targets belong to the unlabeled bundle
|
|
if ((not target_resource_group and not resource_group) or
|
|
# Does it match special "MATCH_ALL" mapping?
|
|
target_resource_group == MATCH_ALL_LABEL or
|
|
# Does it match currently evaluated group?
|
|
target_resource_group == resource_group):
|
|
node = resource_graph_node_map[target]
|
|
resource_spec = node.resource_spec
|
|
if resource_spec:
|
|
resource_specs.append(resource_spec)
|
|
asset_catalog_spec = node.asset_catalog_spec
|
|
if asset_catalog_spec:
|
|
asset_catalog_specs.append(asset_catalog_spec)
|
|
core_data_spec = node.core_data_spec
|
|
if core_data_spec:
|
|
core_data_specs.append(core_data_spec)
|
|
|
|
return resource_specs, asset_catalog_specs, core_data_specs
|