C++: Allow libraries to be exported by any target

Any library should be exportable by any cc_shared_library target regardless of whether the cc_shared_library target is in the same package or a parent package as long as the library author gives permission.

The library author can now do this by writing tags=["exported_by=//foo,//baz"].

PiperOrigin-RevId: 295137965
Change-Id: I4acffd26981fedd6cb0c505e2691da0c70a7b6b0
diff --git a/examples/experimental_cc_shared_library.bzl b/examples/experimental_cc_shared_library.bzl
index 49b9b8e..0ab7ee6 100644
--- a/examples/experimental_cc_shared_library.bzl
+++ b/examples/experimental_cc_shared_library.bzl
@@ -12,6 +12,7 @@
 GraphNodeInfo = provider(
     fields = {
         "children": "Other GraphNodeInfo from dependencies of this target",
+        "exported_by": "Labels of targets that can export the library of this node",
         "label": "Label of the target visited",
     },
 )
@@ -259,10 +260,19 @@
             fail("Trying to export a library already exported by a different shared library: " +
                  str(export.label))
 
-        if not _same_package_or_above(ctx.label, export[GraphNodeInfo].label):
+        can_be_exported = _same_package_or_above(ctx.label, export.label)
+
+        if not can_be_exported:
+            for exported_by in export[GraphNodeInfo].exported_by:
+                target_specified = _is_target_specified(exported_by)
+                exported_by_label = Label(exported_by)
+                if _check_if_target_under_path(ctx.label, exported_by_label, target_specified):
+                    can_be_exported = True
+                    break
+        if not can_be_exported:
             fail(str(export.label) + " cannot be exported from " + str(ctx.label) +
-                 " because " + str(export.label) + " is not in the same package " +
-                 " or a sub-package")
+                 " because it's not in the same package/subpackage or the library " +
+                 "to be exported doesn't have this cc_shared_library in the exported_by tag.")
 
     preloaded_deps_direct_labels = {}
     preloaded_dep_merged_cc_info = None
@@ -348,9 +358,24 @@
             if GraphNodeInfo in dep:
                 children.append(dep[GraphNodeInfo])
 
+    exported_by = []
+    if hasattr(ctx.rule.attr, "tags"):
+        for tag in ctx.rule.attr.tags:
+            if tag.startswith("exported_by=") and len(tag) > 12:
+                for target in tag[12:].split(","):
+                    # Only absolute labels allowed. Targets in same package
+                    # or subpackage can be exported anyway.
+                    if not target.startswith("//") and not target.startswith("@"):
+                        fail("Labels in exported_by of " + str(target) +
+                             " must be absolute.")
+
+                    Label(target)  # Checking synthax is ok.
+                exported_by.append(target)
+
     return [GraphNodeInfo(
         label = ctx.label,
         children = children,
+        exported_by = exported_by,
     )]
 
 graph_structure_aspect = aspect(
diff --git a/examples/test_cc_shared_library/BUILD b/examples/test_cc_shared_library/BUILD
index 93090c4..a28799c 100644
--- a/examples/test_cc_shared_library/BUILD
+++ b/examples/test_cc_shared_library/BUILD
@@ -85,6 +85,7 @@
     exports = [
         "bar",
         "bar2",
+        "@test_repo//:bar",
     ],
 )
 
diff --git a/examples/test_cc_shared_library2/BUILD b/examples/test_cc_shared_library2/BUILD
index 802d60f..1789a5f 100644
--- a/examples/test_cc_shared_library2/BUILD
+++ b/examples/test_cc_shared_library2/BUILD
@@ -4,5 +4,6 @@
     name = "bar",
     srcs = ["bar.cc"],
     hdrs = ["bar.h"],
+    tags = ["exported_by=@rules_cc//examples/test_cc_shared_library:bar_so"],
     visibility = ["//visibility:public"],
 )