Skip to content

Commit

Permalink
chore: refactor deps_indexes separating module resolution from labe…
Browse files Browse the repository at this point in the history
…l resolution (#240)

- Rename `modules` to `modules_by_name`.
- Add `modules_by_label` to support lookup of modules by their primary
Bazel label.
- Made `labels_for_module` public.
- Rename `resolve_module_labels` to `resolve_module`.
- Update `pkginfo_target_deps` to resolve the module, then retrieve the
labels.

Related to #199.
  • Loading branch information
cgrindel authored Feb 24, 2023
1 parent cb8e331 commit 64c3a96
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 219 deletions.
132 changes: 76 additions & 56 deletions swiftpkg/internal/deps_indexes.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,48 @@ def _new_from_json(json_str):
Returns:
A `struct` that contains indexes for external dependencies.
"""
mi = {}
orig_dict = json.decode(json_str)
return _new(
modules = [
_new_module_from_dict(mod_dict)
for mod_dict in orig_dict["modules"]
],
products = [
_new_product_from_dict(prod_dict)
for prod_dict in orig_dict["products"]
],
)

def _new(modules = [], products = []):
modules_by_name = {}
modules_by_label = {}
pi = {}

# buildifier: disable=uninitialized
def _add_module(m):
entries = mi.get(m.name, [])
entries = modules_by_name.get(m.name, [])
entries.append(m)
mi[m.name] = entries
modules_by_name[m.name] = entries
modules_by_label[m.label] = m
if m.name != m.c99name:
entries = mi.get(m.c99name, [])
entries = modules_by_name.get(m.c99name, [])
entries.append(m)
mi[m.c99name] = entries
modules_by_name[m.c99name] = entries

# buildifier: disable=uninitialized
def _add_product(p):
key = _new_product_index_key(p.identity, p.name)
pi[key] = p

orig_dict = json.decode(json_str)
for mod_dict in orig_dict["modules"]:
m = _new_module_from_dict(mod_dict)
_add_module(m)
for prod_dict in orig_dict["products"]:
p = _new_product_from_dict(prod_dict)
_add_product(p)
return _new(
modules = mi,
products = pi,
)
for module in modules:
_add_module(module)
for product in products:
_add_product(product)

def _new(modules = {}, products = {}):
return struct(
modules = modules,
products = products,
modules_by_name = modules_by_name,
modules_by_label = modules_by_label,
products = pi,
)

def _new_module_from_dict(mod_dict):
Expand Down Expand Up @@ -126,33 +134,59 @@ def _labels_for_module(module, depender_src_type):

return labels

def _resolve_module_labels(
def _get_module(deps_index, label):
"""Return the module associated with the specified label.
Args:
deps_index: A `dict` as returned by `deps_indexes.new_from_json`.
label: A `struct` as returned by `bazel_labels.new` or a `string` value
that can be parsed into a Bazel label.
Returns:
If found, a module `struct` as returned by `deps_indexes.new_module`.
Otherwise, `None`.
"""
if type(label) == "string":
label = bazel_labels.parse(label)
return deps_index.modules_by_label.get(label)

def _modules_for_product(deps_index, product):
"""Returns the modules associated with the product.
Args:
deps_index: A `dict` as returned by `deps_indexes.new_from_json`.
product: A `struct` as returned by `deps_indexes.new_product`.
Returns:
A `list` of the modules associated with the product.
"""
return lists.flatten(lists.compact([
_get_module(deps_index, label)
for label in product.target_labels
]))

def _resolve_module(
deps_index,
module_name,
depender_module_name,
preferred_repo_name = None,
restrict_to_repo_names = []):
"""Finds a Bazel label that provides the specified module.
Args:
deps_index: A `dict` as returned by `deps_indexes.new_from_json`.
module_name: The name of the module as a `string`
depender_module_name: The name of the depender module as a `string`.
preferred_repo_name: Optional. If a target in this repository provides
the module, prefer it.
restrict_to_repo_names: Optional. A `list` of repository names to
restrict the match.
Returns:
A `list` of `struct` values as returned by `bazel_labels.new`.
If a module is found, a `struct` as returned by `bazel_labels.new`.
Otherwise, `None`.
"""
modules = deps_index.modules.get(module_name, [])
modules = deps_index.modules_by_name.get(module_name, [])
if len(modules) == 0:
return []

depender_modules = deps_index.modules.get(depender_module_name, [])
if len(depender_modules) == 0:
fail("No depender modules found for {}.".format(depender_module_name))
return None

# If a repo name is provided, prefer that over any other matches
if preferred_repo_name != None:
Expand All @@ -161,14 +195,8 @@ def _resolve_module_labels(
modules,
lambda m: m.label.repository_name == preferred_repo_name,
)
depender_module = lists.find(
depender_modules,
lambda m: m.label.repository_name == preferred_repo_name,
)
if depender_module == None:
depender_module = depender_modules[0]
if module != None:
return _labels_for_module(module, depender_module.src_type)
return module

# If we are meant to only find a match in a set of repo names, then
if len(restrict_to_repo_names) > 0:
Expand All @@ -182,20 +210,11 @@ def _resolve_module_labels(
for m in modules
if sets.contains(repo_names, m.label.repository_name)
]
depender_modules = [
m
for m in depender_modules
if sets.contains(repo_names, m.label.repository_name)
]

# Only labels for the first module.
if len(modules) == 0:
return []
if len(depender_modules) == 0:
fail("No depender modules found for {} in the restricted repos.".format(
depender_module_name,
))
return _labels_for_module(modules[0], depender_modules[0].src_type)
return None
return modules[0]

def _new_product_index_key(identity, name):
return identity.lower() + "|" + name
Expand Down Expand Up @@ -254,24 +273,22 @@ def _new_ctx(deps_index, preferred_repo_name = None, restrict_to_repo_names = []
restrict_to_repo_names = restrict_to_repo_names,
)

def _resolve_module_labels_with_ctx(
def _resolve_module_with_ctx(
deps_index_ctx,
module_name,
depender_module_name):
module_name):
"""Finds a Bazel label that provides the specified module.
Args:
deps_index_ctx: A `struct` as returned by `deps_indexes.new_ctx`.
module_name: The name of the module as a `string`
depender_module_name: The name of the depender module as a `string`.
Returns:
A `list` of `struct` values as returned by `bazel_labels.new`.
If a module is found, a `struct` as returned by `bazel_labels.new`.
Otherwise, `None`.
"""
return _resolve_module_labels(
return _resolve_module(
deps_index = deps_index_ctx.deps_index,
module_name = module_name,
depender_module_name = depender_module_name,
preferred_repo_name = deps_index_ctx.preferred_repo_name,
restrict_to_repo_names = deps_index_ctx.restrict_to_repo_names,
)
Expand All @@ -293,12 +310,15 @@ src_types = struct(

deps_indexes = struct(
find_product = _find_product,
get_module = _get_module,
labels_for_module = _labels_for_module,
modules_for_product = _modules_for_product,
new = _new,
new_ctx = _new_ctx,
new_from_json = _new_from_json,
new_module = _new_module,
new_product = _new_product,
resolve_module_labels = _resolve_module_labels,
resolve_module_labels_with_ctx = _resolve_module_labels_with_ctx,
resolve_module = _resolve_module,
resolve_module_with_ctx = _resolve_module_with_ctx,
resolve_product_labels = _resolve_product_labels,
)
49 changes: 31 additions & 18 deletions swiftpkg/internal/pkginfo_target_deps.bzl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Module for generating data from target dependencies created by `pkginfos`."""

load("@cgrindel_bazel_starlib//bzllib:defs.bzl", "bazel_labels")
load("@cgrindel_bazel_starlib//bzllib:defs.bzl", "bazel_labels", "lists")
load(":bzl_selects.bzl", "bzl_selects")
load(":deps_indexes.bzl", "deps_indexes")
load(":pkginfo_dependencies.bzl", "pkginfo_dependencies")
Expand Down Expand Up @@ -31,33 +31,28 @@ def make_pkginfo_target_deps(bazel_labels):
# This should look for a matching product first. Then, look for a
# module directly?
condition = target_dep.by_name.condition
labels = deps_indexes.resolve_module_labels_with_ctx(
module = deps_indexes.resolve_module_with_ctx(
pkg_ctx.deps_index_ctx,
target_dep.by_name.name,
depender_module_name,
)
if len(labels) == 0:
# Seeing Package.swift files with byName dependencies that
# cannot be resolved (i.e., they do not exist).
# Example `protoc-gen-swift` in
# `https://github.com/grpc/grpc-swift.git`.
# Printing warnings is discouraged. So, just keep moving
# print("""\
# Unable to resolve by_name target dependency for {module_name}.\
# """.format(module_name = target_dep.by_name.name))
pass

# Seeing Package.swift files with byName dependencies that
# cannot be resolved (i.e., they do not exist).
# Example `protoc-gen-swift` in
# `https://github.com/grpc/grpc-swift.git`.
modules = [module] if module != None else []

elif target_dep.target:
condition = target_dep.target.condition
labels = deps_indexes.resolve_module_labels_with_ctx(
module = deps_indexes.resolve_module_with_ctx(
pkg_ctx.deps_index_ctx,
target_dep.target.target_name,
depender_module_name,
)
if len(labels) == 0:
if module == None:
fail("""\
Unable to resolve target reference target dependency for {module_name}.\
""".format(module_name = target_dep.target.target_name))
modules = [module]

elif target_dep.product:
condition = target_dep.product.condition
Expand All @@ -70,24 +65,42 @@ Unable to resolve target reference target dependency for {module_name}.\
fail("""\
Did not find external dependency with name/identity {}.\
""".format(prod_ref.dep_name))
labels = deps_indexes.resolve_product_labels(

product = deps_indexes.find_product(
deps_index = pkg_ctx.deps_index_ctx.deps_index,
identity = dep.identity,
name = prod_ref.product_name,
)
if len(labels) == 0:
if product == None:
fail("""\
Unable to resolve product reference target dependency for product {prod_name} provided by {dep_name}.
""".format(
prod_name = prod_ref.product_name,
dep_name = prod_ref.dep_name,
))
modules = deps_indexes.modules_for_product(
deps_index = pkg_ctx.deps_index_ctx.deps_index,
product = product,
)

else:
fail("""\
Unrecognized target dependency while generating a Bazel dependency label.\
""")

# Find the depender module
depender_module = deps_indexes.resolve_module_with_ctx(
pkg_ctx.deps_index_ctx,
depender_module_name,
)
if depender_module == None:
fail("Unable to find depender module named {}.".format(depender_module_name))

labels = lists.flatten([
deps_indexes.labels_for_module(module, depender_module.src_type)
for module in modules
])

return bzl_selects.new_from_target_dependency_condition(
kind = _target_dep_kind,
labels = [
Expand Down
Loading

0 comments on commit 64c3a96

Please sign in to comment.