From bb9571c02ad9ffd6bf51e3393869dc52ab6d6324 Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Sat, 3 Sep 2022 04:11:39 -0400 Subject: [PATCH 1/2] Add `lib/labels.bzl` --- lib/BUILD | 5 ++++ lib/labels.bzl | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 lib/labels.bzl diff --git a/lib/BUILD b/lib/BUILD index 23280816..d4623257 100644 --- a/lib/BUILD +++ b/lib/BUILD @@ -20,6 +20,11 @@ bzl_library( srcs = ["dicts.bzl"], ) +bzl_library( + name = "labels", + srcs = ["labels.bzl"], +) + bzl_library( name = "partial", srcs = ["partial.bzl"], diff --git a/lib/labels.bzl b/lib/labels.bzl new file mode 100644 index 00000000..fc4beefb --- /dev/null +++ b/lib/labels.bzl @@ -0,0 +1,77 @@ +""" +Skylib module containing additional sugar for resolving or analyzing labels +across workspaces / repositories and packages. +""" + +# Possibly helpful pointers / complaints: +# https://github.com/bazelbuild/bazel/issues/16210 + +def strip_prefix(prefix, value): + if not value.startswith(prefix): + fail("bad") + return value[len(prefix):] + +def qualify_package(label): + """ + Returns a "qualified package" name, that is "@repository//package". + """ + if in_root_workspace(label): + return "//" + label.package + elif label.package == "": + return "@" + label.workspace_name + else: + return "@" + label.workspace_name + "//" + label.package + +def current_qualified_package(): + """ + Gets the current package as "qualified package". + """ + label_str = "//" + native.package_name() + repository = native.repository_name() + if repository == "@": + return "//" + native.package_name() + else: + return repository + "//" + native.package_name() + +def to_label(label_str): + """ + Converts any label string to a Label instance. + Normally, Label() requires absolute paths, so this wraps the constructor to + allow automatic scoping. + This can be used to parse / canonicalize labels. + """ + if label_str.startswith(":"): + label_str = current_qualified_package() + label_str + return Label(label_str) + +def in_workspace(label, workspace): + """True if given Label is in the specified workspace / repository.""" + if workspace.startswith("@"): + fail("Please supply name without @") + return label.workspace_name == workspace + +def in_root_workspace(label): + """True if given Label is in source workspace.""" + return in_workspace(label, "") + +def workspace_name(): + """Repository name, but without leading @.""" + return strip_prefix("@", native.repository_name()) + +def in_current_package(label): + """True if given label is in the current package.""" + return ( + label.workspace_name == workspace_name() and + label.package == native.package_name() + ) + +labels = struct( + strip_prefix = strip_prefix, + qualify_package = qualify_package, + current_qualified_package = current_qualified_package, + to_label = to_label, + in_workspace = in_workspace, + in_root_workspace = in_root_workspace, + workspace_name = workspace_name, + in_current_package = in_current_package, +) From a86f31fb633ea34858f6767cb26f8d297a8bd0f8 Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Mon, 5 Sep 2022 12:51:27 -0400 Subject: [PATCH 2/2] updates from dog-fooding --- lib/labels.bzl | 87 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 12 deletions(-) diff --git a/lib/labels.bzl b/lib/labels.bzl index fc4beefb..38d117db 100644 --- a/lib/labels.bzl +++ b/lib/labels.bzl @@ -22,28 +22,59 @@ def qualify_package(label): else: return "@" + label.workspace_name + "//" + label.package -def current_qualified_package(): +def current_qualified_package(ctx = None): """ Gets the current package as "qualified package". """ - label_str = "//" + native.package_name() - repository = native.repository_name() + label_str = "//" + package_name(ctx) + repository = repository_name(ctx) if repository == "@": - return "//" + native.package_name() + return "//" + package_name(ctx) else: - return repository + "//" + native.package_name() + return repository + "//" + package_name(ctx) -def to_label(label_str): +def to_label(label_str, ctx = None): """ Converts any label string to a Label instance. + Normally, Label() requires absolute paths, so this wraps the constructor to allow automatic scoping. + This can be used to parse / canonicalize labels. """ + if type(label_str) == "Label": + return label_str if label_str.startswith(":"): - label_str = current_qualified_package() + label_str + label_str = current_qualified_package(ctx) + label_str return Label(label_str) +def label_to_str(label, ctx = None): + """ + Converts label to abbreviated string. + + For some reason, Bazel likes showing the more verbose label. + """ + if in_current_package(label, ctx = ctx): + return ":" + label.name + else: + if label.workspace_name == "" and repository_name(ctx) == "": + repository_prefix = "" + else: + repository_prefix = "@" + label.workspace_name + + if label.package == "" and label.name == label.workspace_name: + return repository_prefix + elif ( + label.package == label.name or + label.package.endswith("/" + label.name) + ): + return repository_prefix + "//" + label.package + else: + return str(label) + +def labels_to_str_list(labels, ctx = None): + return [label_to_str(label, ctx) for label in labels] + def in_workspace(label, workspace): """True if given Label is in the specified workspace / repository.""" if workspace.startswith("@"): @@ -54,24 +85,56 @@ def in_root_workspace(label): """True if given Label is in source workspace.""" return in_workspace(label, "") -def workspace_name(): +def repository_name(ctx = None): + if ctx == None: + return native.repository_name() + else: + return ctx.attr.repository_name + +def package_name(ctx = None): + if ctx == None: + return native.package_name() + else: + return ctx.attr.package_name + +def workspace_name(ctx = None): """Repository name, but without leading @.""" - return strip_prefix("@", native.repository_name()) + return strip_prefix("@", repository_name(ctx)) -def in_current_package(label): +def in_current_package(label, ctx = None): """True if given label is in the current package.""" return ( - label.workspace_name == workspace_name() and - label.package == native.package_name() + label.workspace_name == workspace_name(ctx) and + label.package == package_name(ctx) ) +def in_same_package(a, b): + return ( + a.workspace_name == b.workspace_name and + a.package == b.package + ) + +# Attributes for using these functions in analysis phase, where you cannot use +# native.repostiory_name() or native.package_name(). +attrs = { + "repository_name": attr.string(mandatory = True), + "package_name": attr.string(mandatory = True), +} + +"""Rolled-up access.""" labels = struct( strip_prefix = strip_prefix, qualify_package = qualify_package, current_qualified_package = current_qualified_package, to_label = to_label, + label_to_str = label_to_str, + labels_to_str_list = labels_to_str_list, in_workspace = in_workspace, in_root_workspace = in_root_workspace, + package_name = package_name, + repository_name = repository_name, workspace_name = workspace_name, in_current_package = in_current_package, + in_same_package = in_same_package, + attrs = attrs, )