fwd/vendor/cxx/tools/buck/prelude/apple/resource_groups.bzl
John Doty 9c435dc440 Vendor dependencies
Let's see how I like this workflow.
2022-12-19 08:38:22 -08:00

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