Merge pull request #53 from aiuto/from_piper

Mega merge of improvements from Google
diff --git a/.bazelci/tests.yml b/.bazelci/tests.yml
index 9aa764a..812c560 100644
--- a/.bazelci/tests.yml
+++ b/.bazelci/tests.yml
@@ -30,7 +30,11 @@
 
 windows: &windows
   platform: windows
-  <<: *default_tests
+  test_targets:
+    - "//tests/..."
+    - "//examples/..."
+    - "-//examples/manifest/..."
+
 
 
 # The cross product of bazel releases X platforms
diff --git a/README.md b/README.md
index 10209d1..2cf85fb 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,5 @@
 # rules_license
 
-CI:
-[![Build status](https://badge.buildkite.com/e12f23186aa579f1e20fcb612a22cd799239c3134bc38e1aff.svg)](https://buildkite.com/bazel/rules-license)
-
 This repository contains a set of rules and tools for
 - declaring metadata about packages, such as
   - the licenses the package is available under
diff --git a/distro/BUILD b/distro/BUILD
index 0231f13..a79d54b 100644
--- a/distro/BUILD
+++ b/distro/BUILD
@@ -17,7 +17,7 @@
 load("@rules_pkg//pkg/releasing:defs.bzl", "print_rel_notes")
 
 package(
-    default_visibility = ["//visibility:private"],
+    default_visibility = ["//visibility:public"],
     default_applicable_licenses = ["//:license"],
 )
 
diff --git a/examples/README.md b/examples/README.md
index 0afb5ca..fede271 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -9,7 +9,7 @@
 -   SCM: source code management system. These examples assume that
     an organization has a SCM that can enforce ownership restrictions on
     specific folder trees. Targets are divided into BUILD files that are
-    reviewed by engineers vs. those that are reviewed by an organization's
+    reviewed by engineers vs. those that are reviewed by an organizations
     compliance team.
 
 ## Overview
diff --git a/examples/manifest/BUILD b/examples/manifest/BUILD
new file mode 100644
index 0000000..d308a59
--- /dev/null
+++ b/examples/manifest/BUILD
@@ -0,0 +1,41 @@
+load(":android_mock.bzl", "android_binary", "android_library")
+load("@rules_license//tools:test_helpers.bzl", "golden_cmd_test")
+
+
+# These two rules today capture what an android_binary would look like.
+# This rule represents the Android specific code that displays licenses
+# on the display. Note that it does not depend on anything to get the
+# license contents; the implementation of these rules macros handle that
+# detail.
+android_library(
+    name = "licenses",
+    srcs = [
+        "license_display.sh",
+    ],
+    data = [
+      "@rules_license//distro:distro",
+    ],
+)
+
+# This captures how the application would be built. The dependencies of this
+# rule are crawled to identify third-party licenses in use. The macro definition
+# of this rule creates a graph to capture that process of identifying licenses,
+# building the licenses target, and finally invoking the "real" android_binary
+# rule to build the final output with the injected license content.
+android_binary(
+    name = "main",
+    srcs = ["main.sh"],
+    deps = [
+    ],
+    data = [
+        ":licenses",
+    ],
+)
+
+golden_cmd_test(
+    name = "main_test",
+    srcs = [],
+    cmd = "$(location :main)",
+    tools = [":main"],
+    golden = "main_golden.txt",
+)
diff --git a/examples/manifest/android_mock.bzl b/examples/manifest/android_mock.bzl
new file mode 100644
index 0000000..0dee3c9
--- /dev/null
+++ b/examples/manifest/android_mock.bzl
@@ -0,0 +1,61 @@
+load("@rules_license//rules:compliance.bzl", "manifest")
+
+"""This is a proof of concept to show how to modify a macro definition to
+create a sub-graph allowing for build time injection of license information. We
+use Android-inspired rule names since these are a likely candidate for this
+sort of injection."""
+
+def android_library(name, **kwargs):
+    # This is an approximation for demo purposes.
+
+    data = kwargs.pop("data", [])
+    native.filegroup(
+        name = name,
+        srcs = data + kwargs.get("srcs", []),
+    )
+
+    # Inject the data dependency into the library, preserving any other data it has.
+    native.sh_library(
+        name = name + "_w_licenses",
+        data = data + [name + "_manifest.txt"],
+        **kwargs
+    )
+
+def android_binary(name, **kwargs):
+    # Same observation about not being sloppy with mapping deps, but I think the only important attribute
+    # in android_binary is deps, but need to double-check.
+    native.filegroup(
+        name = name + "_no_licenses",
+        srcs = kwargs.get("data", []),
+    )
+
+    mf_name = name + "_manifest"
+    manifest(
+        name = mf_name,
+        deps = [":" + name + "_no_licenses"],
+    )
+
+    # This uses the conditions tool to generate an approximation of a compliance report
+    # to demonstrate how license data can be plumbed and made available at build time.
+    native.genrule(
+        name = "gen_" + name + "_manifest",
+        srcs = [":" + mf_name],
+        outs = ["licenses_manifest.txt"],
+        cmd = "cat $(locations :%s) > $@" % mf_name,
+    )
+
+    # Swap out the :licenses dep for our new :licenses_w_licenses dep
+    newdeps = []
+    deps = kwargs.get("data", [])
+    for dep in deps:
+        if dep == ":licenses":
+            newdeps.append(":licenses_w_licenses")
+        else:
+            newdeps.append(dep)
+    kwargs["data"] = newdeps
+
+    # Compile the executable with the user's originally supplied name, but with the new content.
+    native.sh_binary(
+        name = name,
+        **kwargs
+    )
diff --git a/examples/manifest/license_display.sh b/examples/manifest/license_display.sh
new file mode 100644
index 0000000..f96b3ba
--- /dev/null
+++ b/examples/manifest/license_display.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+function display_licenses {
+  echo -n "Licenses: "
+  cat "$0.runfiles/rules_license/examples/manifest/licenses_manifest.txt"
+  echo
+}
diff --git a/examples/manifest/main.sh b/examples/manifest/main.sh
new file mode 100755
index 0000000..4f80a5c
--- /dev/null
+++ b/examples/manifest/main.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+#source gbash.sh || exit
+
+#source "$RUNFILES/google3/tools/build_defs/license/examples/manifest/license_display.sh"
+source "$0.runfiles/rules_license/examples/manifest/license_display.sh"
+#source module google3/tools/build_defs/license/examples/manifest/license_display.sh
+
+echo "I am a program that uses open source code."
+display_licenses
diff --git a/examples/manifest/main_golden.txt b/examples/manifest/main_golden.txt
new file mode 100644
index 0000000..4469cce
--- /dev/null
+++ b/examples/manifest/main_golden.txt
@@ -0,0 +1,204 @@
+I am a program that uses open source code.
+Licenses: 
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/examples/my_org/compliance/BUILD b/examples/my_org/compliance/BUILD
deleted file mode 100644
index 074b21e..0000000
--- a/examples/my_org/compliance/BUILD
+++ /dev/null
@@ -1,31 +0,0 @@
-# Example license policy definitions.
-
-load("@rules_license//rules:license_policy.bzl", "license_policy")
-
-package(default_visibility = ["//examples:__subpackages__"])
-
-# license_policy rules generally appear in a central location per workspace. They
-# are intermingled with normal target build rules
-license_policy(
-    name = "production_service",
-    conditions = [
-        "notice",
-        "restricted_if_statically_linked",
-    ],
-)
-
-license_policy(
-    name = "mobile_application",
-    conditions = [
-        "notice",
-    ],
-)
-
-license_policy(
-    name = "special_whitelisted_app",
-    # There could be a whitelist of targets here.
-    conditions = [
-        "notice",
-        "whitelist:acme_corp_paid",
-    ],
-)
diff --git a/examples/my_org/licenses/BUILD b/examples/my_org/licenses/BUILD
index d9d5c25..f17bfa3 100644
--- a/examples/my_org/licenses/BUILD
+++ b/examples/my_org/licenses/BUILD
@@ -1,3 +1,16 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
 # Example license kind definitions.
 
 # We expect that all license_kind rules used by an organization exist in a
diff --git a/examples/src/BUILD b/examples/src/BUILD
index 29b6803..ecab5da 100644
--- a/examples/src/BUILD
+++ b/examples/src/BUILD
@@ -1,17 +1,25 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
 # Examples of applications and interactions with licenses
 
+load("@rules_license//rules:compliance.bzl", "check_license", "licenses_used")
 load("@rules_license//examples/vendor/constant_gen:defs.bzl", "constant_gen")
-load("@rules_license//rules:compliance.bzl", "licenses_used")
-load("@rules_license//rules:license_policy_check.bzl", "license_policy_check")
-load("@rules_license//tools:test_helpers.bzl", "golden_test")
 
 cc_binary(
     name = "my_server",
     srcs = ["server.cc"],
-    deps = [
-        ":message",
-        "@rules_license//examples/vendor/libhhgttg",
-    ],
+    deps = [":message"],
 )
 
 # Sample
@@ -21,32 +29,17 @@
     var = "server_message",
 )
 
-license_policy_check(
+# TODO(aiuto): Turn this strictly into a compliance test.
+check_license(
     name = "check_server",
-    policy = "@rules_license//examples/my_org/compliance:production_service",
-    target = ":my_server",
-)
-
-cc_binary(
-    name = "my_violating_server",
-    srcs = ["server.cc"],
+    check_conditions = False,
+    license_texts = "server_licenses.txt",
+    report = "server_report.txt",
     deps = [
-        ":message",
-        "@rules_license//examples/vendor/acme",
-        "@rules_license//examples/vendor/libhhgttg",
+        ":my_server",
     ],
 )
 
-license_policy_check(
-    name = "check_violating_server",
-    policy = "@rules_license//examples/my_org/compliance:production_service",
-    tags = [
-        "manual",
-        "notap",
-    ],
-    target = ":my_violating_server",
-)
-
 #
 # Verify the licenses are what we expect. The golden output shows that
 # :my_server only uses the unencumbered license type.
@@ -57,8 +50,12 @@
     deps = [":my_server"],
 )
 
-golden_test(
-    name = "verify_server_licenses_test",
-    golden = "server_licenses.golden",
-    subject = ":server_licenses.json",
+py_test(
+    name = "server_licenses_test",
+    srcs = ["server_licenses_test.py"],
+    data = [":server_licenses.json"],
+    python_version = "PY3",
+    deps = [
+        "@rules_license//tests:license_test_utils",
+    ],
 )
diff --git a/examples/src/mobile.cc b/examples/src/mobile.cc
deleted file mode 100644
index d15090f..0000000
--- a/examples/src/mobile.cc
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2020 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-
-#include <iostream>
-
-int main(int argc, char* argv[]) {
-  std::cout << "Hello world" << std::endl;
-}
diff --git a/examples/src/server.cc b/examples/src/server.cc
index 8f7990e..8229fc1 100644
--- a/examples/src/server.cc
+++ b/examples/src/server.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 Google LLC
+// Copyright 2022 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -11,8 +11,8 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-
 #include <iostream>
+#include <ostream>
 
 extern const char* server_message;
 
diff --git a/examples/src/server_licenses.golden b/examples/src/server_licenses.golden
deleted file mode 100644
index 57df1b3..0000000
--- a/examples/src/server_licenses.golden
+++ /dev/null
@@ -1,32 +0,0 @@
-[
-  {
-    "rule": "//examples/vendor/constant_gen:license_for_emitted_code",
-    "license_kinds": [
-      {
-        "target": "@//examples/my_org/licenses:unencumbered",
-        "name": "unencumbered",
-        "conditions": []
-      }
-    ],
-    "copyright_notice": "",
-    "package_name": "Trivial Code Generator Output",
-    "package_url": null,
-    "package_version": null,
-    "license_text": "examples/vendor/constant_gen/LICENSE"
-  },
-  {
-    "rule": "//examples/vendor/libhhgttg:license",
-    "license_kinds": [
-      {
-        "target": "@//examples/my_org/licenses:generic_notice",
-        "name": "generic_notice",
-        "conditions": ["notice"]
-      }
-    ],
-    "copyright_notice": "",
-    "package_name": "",
-    "package_url": null,
-    "package_version": null,
-    "license_text": "examples/vendor/libhhgttg/LICENSE"
-  }
-]
diff --git a/examples/src/server_licenses_test.py b/examples/src/server_licenses_test.py
new file mode 100644
index 0000000..c7c30da
--- /dev/null
+++ b/examples/src/server_licenses_test.py
@@ -0,0 +1,46 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Tests for license/examples/src."""
+
+import os
+
+import unittest
+from tests import license_test_utils
+
+
+class ServerLicensesTest(unittest.TestCase):
+
+  def test_has_expected_licenses(self):
+    package_base = license_test_utils.LICENSE_PACKAGE_BASE
+    licenses_info = license_test_utils.load_licenses_info(
+        os.path.join(os.path.dirname(__file__), "server_licenses.json"))
+    licenses_info = license_test_utils.filter_dependencies(
+        licenses_info,
+        target_filter=lambda targ: targ.startswith(package_base),
+        licenses_filter=lambda lic: lic.startswith(package_base))
+
+    expected = {
+        "/examples/src:message_src_": [
+            "/examples/vendor/constant_gen:license_for_emitted_code"
+        ],
+        "/examples/src:message": [
+            "/examples/vendor/constant_gen:license_for_emitted_code"
+        ],
+    }
+    license_test_utils.check_licenses_of_dependencies(
+        self, licenses_info, expected)
+
+
+if __name__ == "__main__":
+  unittest.main()
diff --git a/examples/src/server_report.golden b/examples/src/server_report.golden
index 6d322b1..8be07a0 100644
--- a/examples/src/server_report.golden
+++ b/examples/src/server_report.golden
@@ -1,3 +1,3 @@
-= @rules_license//examples/vendor/constant_gen:license_for_emitted_code
-  kind: @@rules_license//examples/my_org/licenses:unencumbered
+= //examples/vendor/constant_gen:license_for_emitted_code
+  kind: @//examples/my_org/licenses:unencumbered
   conditions: []
diff --git a/examples/vendor/acme/BUILD b/examples/vendor/acme/BUILD
index 488af62..814957d 100644
--- a/examples/vendor/acme/BUILD
+++ b/examples/vendor/acme/BUILD
@@ -1,11 +1,21 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
 # A package with a commercial license.
 
 load("@rules_license//rules:license.bzl", "license")
 
-package(
-    default_applicable_licenses = [":license"],
-    default_visibility = ["//visibility:public"],
-)
+package(default_applicable_licenses = [":license"])
 
 # The default license for an entire package is typically named "license".
 license(
diff --git a/examples/vendor/acme/coyote.cc b/examples/vendor/acme/coyote.cc
index e1e8083..d637855 100644
--- a/examples/vendor/acme/coyote.cc
+++ b/examples/vendor/acme/coyote.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 Google LLC
+// Copyright 2022 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -11,7 +11,6 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-
 bool caught_road_runner() {
   return false;
 }
diff --git a/examples/vendor/constant_gen/BUILD b/examples/vendor/constant_gen/BUILD
index a81885c..e70f489 100644
--- a/examples/vendor/constant_gen/BUILD
+++ b/examples/vendor/constant_gen/BUILD
@@ -1,8 +1,21 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
 # An example of a code generator with a distinct license for the generated code.
 
 load("@rules_license//rules:compliance.bzl", "licenses_used")
-load("@rules_license//rules:license.bzl", "license")
 load("@rules_license//tools:test_helpers.bzl", "golden_test")
+load("@rules_license//rules:license.bzl", "license")
 load(":defs.bzl", "constant_gen")
 
 package(
@@ -13,22 +26,20 @@
 # The default license for an entire package is typically named "license".
 license(
     name = "license",
+    package_name = "Trivial Code Generator",
     license_kinds = [
         "@rules_license//examples/my_org/licenses:generic_restricted",
     ],
     license_text = "LICENSE",
-    package_name = "Trivial Code Generator",
-    package_url = "http://github.com/tgc-fake/tgc.tgz",
-    package_version = "3.14",
 )
 
 license(
     name = "license_for_emitted_code",
     package_name = "Trivial Code Generator Output",
-    license = "LICENSE.on_output",
     license_kinds = [
         "@rules_license//examples/my_org/licenses:unencumbered",
     ],
+    license_text = "LICENSE.on_output",
 )
 
 # The generator itself will be licensed under :license
@@ -69,3 +80,4 @@
     golden = "generated_code_licenses.golden",
     subject = ":generated_code_licenses.json",
 )
+
diff --git a/examples/vendor/constant_gen/constant_generator.py b/examples/vendor/constant_gen/constant_generator.py
index 6bd92b2..432b6be 100644
--- a/examples/vendor/constant_gen/constant_generator.py
+++ b/examples/vendor/constant_gen/constant_generator.py
@@ -1,4 +1,4 @@
-# Copyright 2020 Google LLC
+# Copyright 2022 Google LLC
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -11,8 +11,6 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
-# Lint as: python3
 """A trivial tool to turn a string into a C++ constant.
 
 This is not meant to be useful. It is only to provide an example of a tool that
diff --git a/examples/vendor/constant_gen/defs.bzl b/examples/vendor/constant_gen/defs.bzl
index e54fcee..f3eb715 100644
--- a/examples/vendor/constant_gen/defs.bzl
+++ b/examples/vendor/constant_gen/defs.bzl
@@ -1,19 +1,5 @@
 """A trivial rule to turn a string into a C++ constant."""
 
-# Copyright 2020 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
 def _constant_gen_impl(ctx):
     # Turn text into a C++ constant.
     outputs = [ctx.outputs.src_out]
diff --git a/examples/vendor/constant_gen/generated_code_licenses.golden b/examples/vendor/constant_gen/generated_code_licenses.golden
index 6aef78a..c4c8ef1 100644
--- a/examples/vendor/constant_gen/generated_code_licenses.golden
+++ b/examples/vendor/constant_gen/generated_code_licenses.golden
@@ -1,17 +1,41 @@
 [
   {
-    "rule": "//examples/vendor/constant_gen:license_for_emitted_code",
-    "license_kinds": [
+    "top_level_target": "//examples/vendor/constant_gen:libhello",
+    "dependencies": [
       {
-        "target": "@//examples/my_org/licenses:unencumbered",
-        "name": "unencumbered",
-        "conditions": []
+        "target_under_license": "//examples/vendor/constant_gen:libhello",
+        "licenses": [
+          "//examples/vendor/constant_gen:license_for_emitted_code"
+        ]
+      },
+      {
+        "target_under_license": "//examples/vendor/constant_gen:libhello_src_",
+        "licenses": [
+          "//examples/vendor/constant_gen:license_for_emitted_code"
+        ]
       }
     ],
-    "copyright_notice": "",
-    "package_name": "Trivial Code Generator Output",
-    "package_url": null,
-    "package_version": null,
-    "license_text": "examples/vendor/constant_gen/LICENSE"
+    "licenses": [
+      {
+        "label": "//examples/vendor/constant_gen:license_for_emitted_code",
+        "rule": "//examples/vendor/constant_gen:license_for_emitted_code",
+        "license_kinds": [
+          {
+            "target": "@//examples/my_org/licenses:unencumbered",
+            "name": "unencumbered",
+            "conditions": []
+          }
+        ],
+        "copyright_notice": "",
+        "package_name": "Trivial Code Generator Output",
+        "package_url": "",
+        "package_version": "",
+        "license_text": "examples/vendor/constant_gen/LICENSE.on_output",
+        "used_by": [
+          "//examples/vendor/constant_gen:libhello",
+          "//examples/vendor/constant_gen:libhello_src_"
+        ]
+      }
+    ]
   }
 ]
diff --git a/examples/vendor/constant_gen/generator_licenses.golden b/examples/vendor/constant_gen/generator_licenses.golden
index f6fa349..4b2f175 100644
--- a/examples/vendor/constant_gen/generator_licenses.golden
+++ b/examples/vendor/constant_gen/generator_licenses.golden
@@ -1,17 +1,34 @@
 [
   {
-    "rule": "//examples/vendor/constant_gen:license",
-    "license_kinds": [
+    "top_level_target": "//examples/vendor/constant_gen:constant_generator",
+    "dependencies": [
       {
-        "target": "@//examples/my_org/licenses:generic_restricted",
-        "name": "generic_restricted",
-        "conditions": ["restricted"]
+        "target_under_license": "//examples/vendor/constant_gen:constant_generator",
+        "licenses": [
+          "//examples/vendor/constant_gen:license"
+        ]
       }
     ],
-    "copyright_notice": "",
-    "package_name": "Trivial Code Generator",
-    "package_url": "http://github.com/tgc-fake/tgc.tgz",
-    "package_version": "3.14",
-    "license_text": "examples/vendor/constant_gen/LICENSE"
+    "licenses": [
+      {
+        "label": "//examples/vendor/constant_gen:license",
+        "rule": "//examples/vendor/constant_gen:license",
+        "license_kinds": [
+          {
+            "target": "@//examples/my_org/licenses:generic_restricted",
+            "name": "generic_restricted",
+            "conditions": ["restricted"]
+          }
+        ],
+        "copyright_notice": "",
+        "package_name": "Trivial Code Generator",
+        "package_url": "",
+        "package_version": "",
+        "license_text": "examples/vendor/constant_gen/LICENSE",
+        "used_by": [
+          "//examples/vendor/constant_gen:constant_generator"
+        ]
+      }
+    ]
   }
 ]
diff --git a/examples/vendor/libhhgttg/BUILD b/examples/vendor/libhhgttg/BUILD
index c5da389..44d2d61 100644
--- a/examples/vendor/libhhgttg/BUILD
+++ b/examples/vendor/libhhgttg/BUILD
@@ -1,3 +1,16 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
 # A package with all code under a single license. This is the most common case
 # we expect to see.
 
@@ -5,10 +18,7 @@
 
 # Using a package wide default ensure that all targets are associated with the
 # license.
-package(
-    default_applicable_licenses = [":license"],
-    default_visibility = ["//visibility:public"],
-)
+package(default_applicable_licenses = [":license"])
 
 # The default license for an entire package is typically named "license".
 license(
diff --git a/examples/vendor/libhhgttg/answer.cc b/examples/vendor/libhhgttg/answer.cc
index 8b78f90..440bc62 100644
--- a/examples/vendor/libhhgttg/answer.cc
+++ b/examples/vendor/libhhgttg/answer.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 Google LLC
+// Copyright 2022 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
diff --git a/licenses/generic/BUILD b/licenses/generic/BUILD
index 71880cf..def2334 100644
--- a/licenses/generic/BUILD
+++ b/licenses/generic/BUILD
@@ -86,6 +86,14 @@
     ],
 )
 
+# See: https://opensource.google/docs/thirdparty/licenses/#restricted
+license_kind(
+    name = "restricted",
+    conditions = [
+        "restricted",
+    ],
+)
+
 # See: https://opensource.google/docs/thirdparty/licenses/#ByExceptionOnly
 license_kind(
     name = "by_exception_only",
diff --git a/rules/BUILD b/rules/BUILD
index b4dde6f..1d67059 100644
--- a/rules/BUILD
+++ b/rules/BUILD
@@ -15,6 +15,8 @@
 # limitations under the License.
 """Rules for making license declarations."""
 
+load("@rules_license//rules:licenses_core.bzl", "trace")
+
 package(
     default_applicable_licenses = ["//:license"],
     default_visibility = ["//visibility:public"],
@@ -22,6 +24,15 @@
 
 licenses(["notice"])
 
+# This target controls the value of the traced target used during dependency collection.
+# This value should always be the empty string!
+# Specify this value with a flag, like --@rules_license//rules:trace_target=//target/to:trace
+trace(
+    name = "trace_target",
+    build_setting_default = "",  # TRACE-TARGET-SHOULD-BE-EMPTY
+    visibility = ["//visibility:public"],
+)
+
 filegroup(
     name = "standard_package",
     srcs = glob(["**"]),
diff --git a/rules/check_licenses_shim.bzl b/rules/check_licenses_shim.bzl
new file mode 100644
index 0000000..3dcfe2c
--- /dev/null
+++ b/rules/check_licenses_shim.bzl
@@ -0,0 +1,30 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""This module provides a custom Starlark rule used to create wrappers for targets that
+can have blaze build --check_licenses executed against them."""
+
+def _shim_rule_impl(ctx):
+    # This rule doesn't need to return anything. It only exists to propagate the dependency supplied
+    # by the label_flag
+    return []
+
+shim_rule = rule(
+    doc = """This rule exists to configure a dependent target via label. An instantiation of this
+    rule is then used as a dependency for the legacy_check_target rule, which can be built with --check_licenses
+    to get the effect of running --check_licenses on an arbitrary target which may or may not have a distribs
+    attribute""",
+    implementation = _shim_rule_impl,
+    # The definition of this attribute creates a dependency relationship on the manually provided label.
+    attrs = {"target": attr.label(default = ":check_licenses_target")},
+)
diff --git a/rules/compliance.bzl b/rules/compliance.bzl
index 343759a..1792454 100644
--- a/rules/compliance.bzl
+++ b/rules/compliance.bzl
@@ -1,4 +1,4 @@
-# Copyright 2020 Google LLC
+# Copyright 2022 Google LLC
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -11,40 +11,30 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
-"""Proof of concept. License compliance checking."""
+"""License compliance checking."""
 
 load(
     "@rules_license//rules:gather_licenses_info.bzl",
     "gather_licenses_info",
+    "gather_licenses_info_and_write",
     "write_licenses_info",
 )
 load(
     "@rules_license//rules:providers.bzl",
-    "LicensesInfo",
+    "TransitiveLicensesInfo",
 )
 
-# Debugging verbosity
-_VERBOSITY = 0
-
-def _debug(loglevel, msg):
-    if _VERBOSITY > loglevel:
-        print(msg)  # buildifier: disable=print
-
+# This rule is proof of concept, and may not represent the final
+# form of a rule for compliance validation.
 def _check_license_impl(ctx):
     # Gather all licenses and write information to one place
 
-    _debug(0, "Check license: %s" % ctx.label)
-
     licenses_file = ctx.actions.declare_file("_%s_licenses_info.json" % ctx.label.name)
     write_licenses_info(ctx, ctx.attr.deps, licenses_file)
 
     license_files = []
     if ctx.outputs.license_texts:
-        for dep in ctx.attr.deps:
-            if LicensesInfo in dep:
-                for license in dep[LicensesInfo].licenses.to_list():
-                    license_files.append(license.license_text)
+        license_files = get_licenses_mapping(ctx.attr.deps).keys()
 
     # Now run the checker on it
     inputs = [licenses_file]
@@ -75,11 +65,11 @@
 _check_license = rule(
     implementation = _check_license_impl,
     attrs = {
-        "check_conditions": attr.bool(default = True, mandatory = False),
-        "copyright_notices": attr.output(mandatory = False),
         "deps": attr.label_list(
             aspects = [gather_licenses_info],
         ),
+        "check_conditions": attr.bool(default = True, mandatory = False),
+        "copyright_notices": attr.output(mandatory = False),
         "license_texts": attr.output(mandatory = False),
         "report": attr.output(mandatory = True),
         "_checker": attr.label(
@@ -91,21 +81,56 @@
     },
 )
 
+# TODO(b/152546336): Update the check to take a pointer to a condition list.
 def check_license(**kwargs):
     _check_license(**kwargs)
 
+def _manifest_impl(ctx):
+    # Gather all licenses and make it available as deps for downstream rules
+    # Additionally write the list of license filenames to a file that can
+    # also be used as an input to downstream rules.
+    licenses_file = ctx.actions.declare_file(ctx.attr.out.name)
+    mappings = get_licenses_mapping(ctx.attr.deps, ctx.attr.warn_on_legacy_licenses)
+    ctx.actions.write(
+        output = licenses_file,
+        content = "\n".join([",".join([f.path, p]) for (f, p) in mappings.items()]),
+    )
+    return [DefaultInfo(files = depset(mappings.keys()))]
+
+_manifest = rule(
+    implementation = _manifest_impl,
+    doc = """Internal tmplementation method for manifest().""",
+    attrs = {
+        "deps": attr.label_list(
+            doc = """List of targets to collect license files for.""",
+            aspects = [gather_licenses_info],
+        ),
+        "out": attr.output(
+            doc = """Output file.""",
+            mandatory = True,
+        ),
+        "warn_on_legacy_licenses": attr.bool(default = False),
+    },
+)
+
+def manifest(name, deps, out = None, **kwargs):
+    if not out:
+        out = name + ".manifest"
+
+    _manifest(name = name, deps = deps, out = out, **kwargs)
+
 def _licenses_used_impl(ctx):
-    """Gather all licenses and make it available as JSON."""
+    # Gather all licenses and make it available as JSON
     write_licenses_info(ctx, ctx.attr.deps, ctx.outputs.out)
     return [DefaultInfo(files = depset([ctx.outputs.out]))]
 
 _licenses_used = rule(
     implementation = _licenses_used_impl,
-    doc = """Internal implementation method for licenses_used().""",
+    doc = """Internal tmplementation method for licenses_used().""",
     attrs = {
         "deps": attr.label_list(
             doc = """List of targets to collect LicenseInfo for.""",
-            aspects = [gather_licenses_info],
+            aspects = [gather_licenses_info_and_write],
         ),
         "out": attr.output(
             doc = """Output file.""",
@@ -114,6 +139,38 @@
     },
 )
 
+def get_licenses_mapping(deps, warn = False):
+    """Creates list of entries representing all licenses for the deps.
+
+    Args:
+
+      deps: a list of deps which should have TransitiveLicensesInfo providers.
+            This requires that you have run the gather_licenses_info
+            aspect over them
+
+      warn: boolean, if true, display output about legacy targets that need
+            update
+
+    Returns:
+      {File:package_name}
+    """
+    tls = []
+    for dep in deps:
+        lds = dep[TransitiveLicensesInfo].licenses
+        tls.append(lds)
+
+    ds = depset(transitive = tls)
+
+    # Ignore any legacy licenses that may be in the report
+    mappings = {}
+    for lic in ds.to_list():
+        if type(lic.license_text) == "File":
+            mappings[lic.license_text] = lic.package_name
+        elif warn:
+            print("Legacy license %s not included, rule needs updating" % lic.license_text)
+
+    return mappings
+
 def licenses_used(name, deps, out = None, **kwargs):
     """Collects LicensedInfo providers for a set of targets and writes as JSON.
 
diff --git a/rules/default_license.bzl b/rules/default_license.bzl
deleted file mode 100644
index 57a7147..0000000
--- a/rules/default_license.bzl
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright 2020 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""Proof of concept. License restriction."""
-
-load(
-    "@rules_license//rules:providers.bzl",
-    "LicenseInfo",
-    "LicensesInfo",
-)
-
-# An experiment to provide license defaults via a rule. This is far from
-# working and should not be considered part of the current design.
-#
-
-def _default_licenses_impl(ctx):
-    licenses = []
-    for dep in ctx.attr.deps:
-        if LicenseInfo in dep:
-            licenses.append(dep[LicenseInfo])
-    return [LicensesInfo(licenses = licenses)]
-
-_default_licenses = rule(
-    implementation = _default_licenses_impl,
-    attrs = {
-        "conditions": attr.string_list(
-            doc = "TBD",
-        ),
-        "deps": attr.label_list(
-            mandatory = True,
-            doc = "Licenses",
-            providers = [LicenseInfo],
-            cfg = "exec",
-        ),
-    },
-)
-
-# buildifier: disable=unnamed-macro
-def default_licenses(licenses, conditions = None):
-    _default_licenses(
-        name = "__default_licenses",
-        deps = ["%s_license" % license for license in licenses],
-        conditions = conditions,
-    )
diff --git a/rules/filtered_rule_kinds.bzl b/rules/filtered_rule_kinds.bzl
new file mode 100644
index 0000000..1d6f01c
--- /dev/null
+++ b/rules/filtered_rule_kinds.bzl
@@ -0,0 +1,47 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Filtered rule kinds for aspect inspection.
+The format of this dictionary is:
+
+  rule_name: [attr, attr, ...]
+
+Only filters for rules that are part of the Bazel distribution should be added
+to this file. Other filters should be added in user_filtered_rule_kinds.bzl
+
+Attributes are either the explicit list of attributes to filter, or '_*' which
+would ignore all attributes prefixed with a _.
+"""
+
+# Rule kinds with attributes the aspect currently needs to ignore
+aspect_filters = {
+    "*": ["linter"],
+    "_constant_gen": ["_generator"],
+    "cc_binary": ["_*"],
+    "cc_embed_data": ["_*"],
+    "cc_grpc_library": ["_*"],
+    "cc_library": ["_*"],
+    "cc_toolchain_alias": ["_cc_toolchain"],
+    "genrule": ["tools", "exec_tools", "toolchains"],
+    "genyacc": ["_*"],
+    "go_binary": ["_*"],
+    "go_library": ["_*"],
+    "go_wrap_cc": ["_*"],
+    "java_binary": ["_*", "plugins", "exported_plugins"],
+    "java_library": ["plugins", "exported_plugins"],
+    "java_wrap_cc": ["_cc_toolchain", "swig_top"],
+    "py_binary": ["_*"],
+    "py_extension": ["_cc_toolchain"],
+    "sh_binary": ["_bash_binary"],
+}
diff --git a/rules/gather_licenses_info.bzl b/rules/gather_licenses_info.bzl
index 9a91229..d2d9df5 100644
--- a/rules/gather_licenses_info.bzl
+++ b/rules/gather_licenses_info.bzl
@@ -1,4 +1,4 @@
-# Copyright 2020 Google LLC
+# Copyright 2022 Google LLC
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -11,21 +11,22 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
 """Rules and macros for collecting LicenseInfo providers."""
 
 load(
+    "@rules_license//rules:licenses_core.bzl",
+    "TraceInfo",
+    "gather_licenses_info_common",
+    "should_traverse",
+)
+load(
     "@rules_license//rules:providers.bzl",
-    "LicenseInfo",
-    "LicensesInfo",
+    "TransitiveLicensesInfo",
 )
 
-# Debugging verbosity
-_VERBOSITY = 0
-
-def _debug(loglevel, msg):
-    if _VERBOSITY > loglevel:
-        print(msg)  # buildifier: disable=print
+# Definition for compliance namespace, used for filtering licenses
+# based on the namespace to which they belong.
+NAMESPACES = ["compliance"]
 
 def _strip_null_repo(label):
     """Removes the null repo name (e.g. @//) from a string.
@@ -37,50 +38,86 @@
         return s[1:]
     return s
 
-def _get_transitive_licenses(deps, licenses, trans):
-    for dep in deps:
-        if LicenseInfo in dep:
-            license = dep[LicenseInfo]
-            _debug(1, "  depends on license: %s" % license.rule)
-            licenses.append(license)
-        if LicensesInfo in dep:
-            license_list = dep[LicensesInfo].licenses
-            if license_list:
-                _debug(1, "  transitively depends on: %s" % licenses)
-                trans.append(license_list)
-
 def _gather_licenses_info_impl(target, ctx):
-    licenses = []
-    trans = []
-    if hasattr(ctx.rule.attr, "applicable_licenses"):
-        _get_transitive_licenses(ctx.rule.attr.applicable_licenses, licenses, trans)
-    if hasattr(ctx.rule.attr, "deps"):
-        _get_transitive_licenses(ctx.rule.attr.deps, licenses, trans)
-    if hasattr(ctx.rule.attr, "srcs"):
-        _get_transitive_licenses(ctx.rule.attr.srcs, licenses, trans)
-    return [LicensesInfo(licenses = depset(tuple(licenses), transitive = trans))]
+    return gather_licenses_info_common(target, ctx, TransitiveLicensesInfo, NAMESPACES, should_traverse)
 
 gather_licenses_info = aspect(
-    doc = """Collects LicenseInfo providers into a single LicensesInfo provider.""",
+    doc = """Collects LicenseInfo providers into a single TransitiveLicensesInfo provider.""",
     implementation = _gather_licenses_info_impl,
-    attr_aspects = ["applicable_licenses", "deps", "srcs"],
+    attr_aspects = ["*"],
+    attrs = {
+        "_trace": attr.label(default = "@rules_license//rules:trace_target"),
+    },
+    provides = [TransitiveLicensesInfo],
     apply_to_generating_rules = True,
 )
 
-def _quotes_or_null(s):
-    if not s:
-        return "null"
-    return '"%s"' % s
+def _write_licenses_info_impl(target, ctx):
+    """Write transitive license info into a JSON file
+
+    Args:
+      target: The target of the aspect.
+      ctx: The aspect evaluation context.
+
+    Returns:
+      OutputGroupInfo
+    """
+
+    if not TransitiveLicensesInfo in target:
+        return [OutputGroupInfo(licenses = depset())]
+    info = target[TransitiveLicensesInfo]
+    outs = []
+
+    # If the result doesn't contain licenses, we simply return the provider
+    if not hasattr(info, "target_under_license"):
+        return [OutputGroupInfo(licenses = depset())]
+
+    # Write the output file for the target
+    name = "%s_licenses_info.json" % ctx.label.name
+    content = "[\n%s\n]\n" % ",\n".join(licenses_info_to_json(info))
+    out = ctx.actions.declare_file(name)
+    ctx.actions.write(
+        output = out,
+        content = content,
+    )
+    outs.append(out)
+
+    if ctx.attr._trace[TraceInfo].trace:
+        trace = ctx.actions.declare_file("%s_trace_info.json" % ctx.label.name)
+        ctx.actions.write(output = trace, content = "\n".join(info.traces))
+        outs.append(trace)
+
+    return [OutputGroupInfo(licenses = depset(outs))]
+
+gather_licenses_info_and_write = aspect(
+    doc = """Collects TransitiveLicensesInfo providers and writes JSON representation to a file.
+
+    Usage:
+      blaze build //some:target \
+          --aspects=@rules_license//rules:gather_licenses_info.bzl%gather_licenses_info_and_write
+          --output_groups=licenses
+    """,
+    implementation = _write_licenses_info_impl,
+    attr_aspects = ["*"],
+    attrs = {
+        "_trace": attr.label(default = "@rules_license//rules:trace_target"),
+    },
+    provides = [OutputGroupInfo],
+    requires = [gather_licenses_info],
+    apply_to_generating_rules = True,
+)
 
 def write_licenses_info(ctx, deps, json_out):
-    """Writes LicensesInfo providers for a set of targets as JSON.
+    """Writes TransitiveLicensesInfo providers for a set of targets as JSON.
 
-    TODO(aiuto): Document JSON schema.
+    TODO(aiuto): Document JSON schema. But it is under development, so the current
+    best place to look is at tests/hello_licenses.golden.
 
     Usage:
       write_licenses_info must be called from a rule implementation, where the
-      rule has run the gather_licenses_info aspect on its deps to collect the
-      transitive closure of LicenseInfo providers into a LicenseInfo provider.
+      rule has run the gather_licenses_info aspect on its deps to
+      collect the transitive closure of LicenseInfo providers into a
+      LicenseInfo provider.
 
       foo = rule(
         implementation = _foo_impl,
@@ -96,51 +133,116 @@
 
     Args:
       ctx: context of the caller
-      deps: a list of deps which should have LicensesInfo providers.
+      deps: a list of deps which should have TransitiveLicensesInfo providers.
             This requires that you have run the gather_licenses_info
             aspect over them
       json_out: output handle to write the JSON info
     """
-
-    rule_template = """  {{
-    "rule": "{rule}",
-    "license_kinds": [{kinds}
-    ],
-    "copyright_notice": "{copyright_notice}",
-    "package_name": "{package_name}",
-    "package_url": {package_url},
-    "package_version": {package_version},
-    "license_text": "{license_text}"\n  }}"""
-
-    kind_template = """
-      {{
-        "target": "{kind_path}",
-        "name": "{kind_name}",
-        "conditions": {kind_conditions}
-      }}"""
-
     licenses = []
     for dep in deps:
-        if LicensesInfo in dep:
-            for license in dep[LicensesInfo].licenses.to_list():
-                _debug(0, "  Requires license: %s" % license)
-                kinds = []
-                for kind in license.license_kinds:
-                    kinds.append(kind_template.format(
-                        kind_name = kind.name,
-                        kind_path = kind.label,
-                        kind_conditions = kind.conditions,
-                    ))
-                licenses.append(rule_template.format(
-                    rule = _strip_null_repo(license.rule),
-                    copyright_notice = license.copyright_notice,
-                    package_name = license.package_name,
-                    package_url = _quotes_or_null(license.package_url),
-                    package_version = _quotes_or_null(license.package_version),
-                    license_text = license.license_text.path,
-                    kinds = ",\n".join(kinds),
-                ))
+        if TransitiveLicensesInfo in dep:
+            licenses.extend(licenses_info_to_json(dep[TransitiveLicensesInfo]))
     ctx.actions.write(
         output = json_out,
         content = "[\n%s\n]\n" % ",\n".join(licenses),
     )
+
+def licenses_info_to_json(licenses_info):
+    """Render a single LicenseInfo provider to JSON
+
+    Args:
+      licenses_info: A LicenseInfo.
+
+    Returns:
+      [(str)] list of LicenseInfo values rendered as JSON.
+    """
+
+    main_template = """  {{
+    "top_level_target": "{top_level_target}",
+    "dependencies": [{dependencies}
+    ],
+    "licenses": [{licenses}
+    ]\n  }}"""
+
+    dep_template = """
+      {{
+        "target_under_license": "{target_under_license}",
+        "licenses": [
+          {licenses}
+        ]
+      }}"""
+
+    # TODO(aiuto): 'rule' is a duplicate of 'label' until old users are transitioned
+    license_template = """
+      {{
+        "label": "{label}",
+        "rule": "{label}",
+        "license_kinds": [{kinds}
+        ],
+        "copyright_notice": "{copyright_notice}",
+        "package_name": "{package_name}",
+        "package_url": "{package_url}",
+        "package_version": "{package_version}",
+        "license_text": "{license_text}",
+        "used_by": [
+          {used_by}
+        ]
+      }}"""
+
+    kind_template = """
+          {{
+            "target": "{kind_path}",
+            "name": "{kind_name}",
+            "conditions": {kind_conditions}
+          }}"""
+
+    # Build reverse map of license to user
+    used_by = {}
+    for dep in licenses_info.deps.to_list():
+        # Undo the concatenation applied when stored in the provider.
+        dep_licenses = dep.licenses.split(",")
+        for license in dep_licenses:
+            if license not in used_by:
+                used_by[license] = []
+            used_by[license].append(_strip_null_repo(dep.target_under_license))
+
+    all_licenses = []
+    for license in sorted(licenses_info.licenses.to_list(), key = lambda x: x.label):
+        kinds = []
+        for kind in sorted(license.license_kinds, key = lambda x: x.name):
+            kinds.append(kind_template.format(
+                kind_name = kind.name,
+                kind_path = kind.label,
+                kind_conditions = kind.conditions,
+            ))
+
+        if license.license_text:
+            # Special handling for synthetic LicenseInfo
+            text_path = (license.license_text.package + "/" + license.license_text.name if type(license.license_text) == "Label" else license.license_text.path)
+            all_licenses.append(license_template.format(
+                copyright_notice = license.copyright_notice,
+                kinds = ",".join(kinds),
+                license_text = text_path,
+                package_name = license.package_name,
+                package_url = license.package_url,
+                package_version = license.package_version,
+                label = _strip_null_repo(license.label),
+                used_by = ",\n          ".join(sorted(['"%s"' % x for x in used_by[str(license.label)]])),
+            ))
+
+    all_deps = []
+    for dep in sorted(licenses_info.deps.to_list(), key = lambda x: x.target_under_license):
+        licenses_used = []
+
+        # Undo the concatenation applied when stored in the provider.
+        dep_licenses = dep.licenses.split(",")
+        all_deps.append(dep_template.format(
+            target_under_license = _strip_null_repo(dep.target_under_license),
+            licenses = ",\n          ".join(sorted(['"%s"' % _strip_null_repo(x) for x in dep_licenses])),
+        ))
+
+    return [main_template.format(
+        top_level_target = _strip_null_repo(licenses_info.target_under_license),
+        dependencies = ",".join(all_deps),
+        licenses = ",".join(all_licenses),
+    )]
diff --git a/rules/license.bzl b/rules/license.bzl
index f726be1..183e106 100644
--- a/rules/license.bzl
+++ b/rules/license.bzl
@@ -1,4 +1,4 @@
-# Copyright 2020 Google LLC
+# Copyright 2022 Google LLC
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -11,47 +11,25 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+"""Rules for declaring the compliance licenses used by a package.
 
-"""Rules for declaring the licenses used by a package."""
+See: go/license-checking-v2
+"""
 
 load(
     "@rules_license//rules:providers.bzl",
-    "LicenseInfo",
     "LicenseKindInfo",
 )
-
-# Debugging verbosity
-_VERBOSITY = 0
-
-def _debug(loglevel, msg):
-    if _VERBOSITY > loglevel:
-        print(msg)  # buildifier: disable=print
-
-#
-# license()
-#
-
-def _license_impl(ctx):
-    provider = LicenseInfo(
-        license_kinds = tuple([k[LicenseKindInfo] for k in ctx.attr.license_kinds]),
-        copyright_notice = ctx.attr.copyright_notice,
-        package_name = ctx.attr.package_name,
-        package_url = ctx.attr.package_url,
-        package_version = ctx.attr.package_version,
-        license_text = ctx.file.license_text,
-        rule = ctx.label,
-    )
-    _debug(0, provider)
-    return [provider]
+load(
+    "@rules_license//rules:license_impl.bzl",
+    "license_rule_impl",
+)
 
 _license = rule(
-    implementation = _license_impl,
+    implementation = license_rule_impl,
     attrs = {
-        "copyright_notice": attr.string(
-            doc = "Copyright notice.",
-        ),
         "license_kinds": attr.label_list(
-            mandatory = True,
+            mandatory = False,
             doc = "License kind(s) of this license. If multiple license kinds are" +
                   " listed in the LICENSE file, and they all apply, then all" +
                   " should be listed here. If the user can choose a single one" +
@@ -59,6 +37,9 @@
             providers = [LicenseKindInfo],
             cfg = "exec",
         ),
+        "copyright_notice": attr.string(
+            doc = "Copyright notice.",
+        ),
         "license_text": attr.label(
             allow_single_file = True,
             default = "LICENSE",
@@ -80,51 +61,66 @@
                   " by an applicatation.  It should be a value that" +
                   " increases over time, rather than a commit hash."
         ),
+        "namespace": attr.string(
+            doc = "A human readable name used to organize licenses into categories." +
+                  " This is used in google3 to differentiate third party licenses used" +
+                  " for compliance versus internal licenses used by SLAsan for internal" +
+                  " teams' SLAs.",
+        ),
     },
 )
 
 # buildifier: disable=function-docstring-args
-def license(name,
-            copyright_notice = None,
-            license_kinds = None,
-            license_text = None,
-            package_name = None,
-            package_url = None,
-            package_version = None,
-            tags = None,
-            **kwargs):
+def license(
+        name,
+        license_text = "LICENSE",
+        visibility = ["//visibility:public"],
+        license_kind = None,
+        license_kinds = None,
+        copyright_notice = None,
+        package_name = None,
+        package_url = None,
+        package_version = None,
+        namespace = "compliance",
+        tags = []):
     """Wrapper for license rule.
 
     Args:
       name: str target name.
+      license_text: str Filename of the license file
+      visibility: list(label) visibility spec
+      license_kind: label a single license_kind. Only one of license_kind or license_kinds may
+                    be specified
       license_kinds: list(label) list of license_kind targets.
-      license_kind: label a single license_kind. Only one of license_kind or
-                    license_kinds may be specified
       copyright_notice: str Copyright notice associated with this package.
-      package_name: str A human readable name identifying this package. This
-                    may be used to produce an index of OSS packages used by
-                    an applicatation.
-      package_url: The URL this instance was downloaded from.
-      package_version: The version number of this package. This should be a
-                       value that increases over time, rather than a commit
-                       hash.
-      kwargs: Other things may be specified, but they are explicitly ignored.
+      package_name : str A human readable name identifying this package. This
+                     may be used to produce an index of OSS packages used by
+                     an application.
+      tags: list(str) tags applied to the rule
     """
-    single_kind = kwargs.pop("license_kind", default = None)
-    if single_kind:
+    if license_kind:
         if license_kinds:
             fail("Can not use both license_kind and license_kinds")
-        license_kinds = [single_kind]
-    tags = tags or []
+        license_kinds = [license_kind]
+
+    # Make sure the file exists as named in the rule. A glob expression that
+    # expands to the name of the file is not acceptable.
+    srcs = native.glob([license_text])
+    if len(srcs) != 1 or srcs[0] != license_text:
+        fail("Specified license file doesn't exist: %s" % license_text)
+
+
     _license(
         name = name,
         license_kinds = license_kinds,
-        license_text = license_text or "LICENSE",
+        license_text = license_text,
         copyright_notice = copyright_notice,
         package_name = package_name,
         package_url = package_url,
         package_version = package_version,
+        namespace = namespace,
         applicable_licenses = [],
+        visibility = visibility,
         tags = tags,
-        visibility = ["//visibility:public"],
+        testonly = 0,
     )
diff --git a/rules/license_impl.bzl b/rules/license_impl.bzl
new file mode 100644
index 0000000..c506953
--- /dev/null
+++ b/rules/license_impl.bzl
@@ -0,0 +1,82 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Rules for declaring the licenses used by a package.
+
+See: go/license-checking-v2
+"""
+
+load(
+    "@rules_license//rules:providers.bzl",
+    "LicenseInfo",
+    "LicenseKindInfo",
+)
+
+# Debugging verbosity
+_VERBOSITY = 0
+
+def _debug(loglevel, msg):
+    if _VERBOSITY > loglevel:
+        print(msg)  # buildifier: disable=print
+
+#
+# license()
+#
+
+def license_rule_impl(ctx):
+    provider = LicenseInfo(
+        license_kinds = tuple([k[LicenseKindInfo] for k in ctx.attr.license_kinds]),
+        copyright_notice = ctx.attr.copyright_notice,
+        package_name = ctx.attr.package_name or ctx.build_file_path.rstrip("/BUILD"),
+        package_url = ctx.attr.package_url,
+        package_version = ctx.attr.package_version,
+        license_text = ctx.file.license_text,
+        label = ctx.label,
+        namespace = ctx.attr.namespace,
+    )
+    _debug(0, provider)
+    return [provider]
+
+license_impl = rule(
+    implementation = license_rule_impl,
+    attrs = {
+        "license_kinds": attr.label_list(
+            mandatory = False,
+            doc = "License kind(s) of this license. If multiple license kinds are" +
+                  " listed in the LICENSE file, and they all apply, then all" +
+                  " should be listed here. If the user can choose a single one" +
+                  " of many, then only list one here.",
+            providers = [LicenseKindInfo],
+            cfg = "exec",
+        ),
+        "copyright_notice": attr.string(
+            doc = "Copyright notice.",
+        ),
+        "license_text": attr.label(
+            allow_single_file = True,
+            default = "LICENSE",
+            doc = "The license file.",
+        ),
+        "package_name": attr.string(
+            doc = "A human readable name identifying this package." +
+                  " This may be used to produce an index of OSS packages used by" +
+                  " an applicatation.",
+        ),
+        "namespace": attr.string(
+            doc = "A human readable name used to organize licenses into categories." +
+                  " This is used in google3 to differentiate third party licenses used" +
+                  " for compliance versus internal licenses used by SLAsan for internal" +
+                  " teams' SLAs.",
+        ),
+    },
+)
diff --git a/rules/license_kind.bzl b/rules/license_kind.bzl
index 47b7639..5c8022e 100644
--- a/rules/license_kind.bzl
+++ b/rules/license_kind.bzl
@@ -1,4 +1,4 @@
-# Copyright 2020 Google LLC
+# Copyright 2022 Google LLC
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -11,7 +11,6 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
 """Proof of concept. License restriction."""
 
 load("@rules_license//rules:providers.bzl", "LicenseKindInfo")
@@ -30,6 +29,7 @@
             ctx.label.package,
             ctx.label.name,
         ),
+        long_name = ctx.attr.long_name,
         conditions = ctx.attr.conditions,
     )
     return [provider]
@@ -37,15 +37,16 @@
 _license_kind = rule(
     implementation = _license_kind_impl,
     attrs = {
-        "canonical_text": attr.label(
-            doc = "File containing the canonical text for this license. Must be UTF-8 encoded.",
-            allow_single_file = True,
-        ),
         "conditions": attr.string_list(
             doc = "Conditions to be met when using software under this license." +
                   "  Conditions are defined by the organization using this license.",
             mandatory = True,
         ),
+        "canonical_text": attr.label(
+            doc = "File containing the canonical text for this license. Must be UTF-8 encoded.",
+            allow_single_file = True,
+        ),
+        "long_name": attr.string(doc = "Human readable long name of license."),
         "url": attr.string(doc = "URL pointing to canonical license definition"),
     },
 )
@@ -53,6 +54,8 @@
 def license_kind(name, **kwargs):
     if "conditions" not in kwargs:
         kwargs["conditions"] = []
+    if "long_name" not in kwargs:
+        kwargs["long_name"] = name
     _license_kind(
         name = name,
         applicable_licenses = [],
diff --git a/rules/license_policy.bzl b/rules/license_policy.bzl
deleted file mode 100644
index 0539301..0000000
--- a/rules/license_policy.bzl
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright 2020 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""license_policy rule.
-
-A license_policy names a set of conditions allowed in the union of all
-license_kinds use by a target. The name of the rule is typically an
-application type (e.g. production_server, mobile_application, ...)
-
-"""
-
-load("@rules_license//rules:license_policy_provider.bzl", "LicensePolicyInfo")
-
-def _license_policy_impl(ctx):
-    provider = LicensePolicyInfo(
-        name = ctx.attr.name,
-        label = "@%s//%s:%s" % (
-            ctx.label.workspace_name,
-            ctx.label.package,
-            ctx.label.name,
-        ),
-        conditions = ctx.attr.conditions,
-    )
-    return [provider]
-
-_license_policy = rule(
-    implementation = _license_policy_impl,
-    attrs = {
-        "conditions": attr.string_list(
-            doc = "Conditions to be met when using software under this license." +
-                  "  Conditions are defined by the organization using this license.",
-            mandatory = True,
-        ),
-    },
-)
-
-def license_policy(name, conditions):
-    _license_policy(
-        name = name,
-        conditions = conditions,
-        applicable_licenses = [],
-    )
diff --git a/rules/license_policy_check.bzl b/rules/license_policy_check.bzl
deleted file mode 100644
index 49ab207..0000000
--- a/rules/license_policy_check.bzl
+++ /dev/null
@@ -1,80 +0,0 @@
-# Copyright 2020 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""License compliance checking at analysis time."""
-
-load(
-    "@rules_license//rules:gather_licenses_info.bzl",
-    "gather_licenses_info",
-)
-load(
-    "@rules_license//rules:license_policy_provider.bzl",
-    "LicensePolicyInfo",
-)
-load(
-    "@rules_license//rules:providers.bzl",
-    "LicensesInfo",
-)
-
-def _license_policy_check_impl(ctx):
-    policy = ctx.attr.policy[LicensePolicyInfo]
-    allowed_conditions = policy.conditions
-    if LicensesInfo in ctx.attr.target:
-        for license in ctx.attr.target[LicensesInfo].licenses.to_list():
-            for kind in license.license_kinds:
-                # print(kind.conditions)
-                for condition in kind.conditions:
-                    if condition not in allowed_conditions:
-                        fail("Condition %s violates policy %s" % (
-                            condition,
-                            policy.label,
-                        ))
-    return [DefaultInfo()]
-
-_license_policy_check = rule(
-    implementation = _license_policy_check_impl,
-    doc = """Internal implementation method for license_policy_check().""",
-    attrs = {
-        "policy": attr.label(
-            doc = """Policy definition.""",
-            mandatory = True,
-            providers = [LicensePolicyInfo],
-        ),
-        "target": attr.label(
-            doc = """Target to collect LicenseInfo for.""",
-            aspects = [gather_licenses_info],
-            mandatory = True,
-            allow_single_file = True,
-        ),
-    },
-)
-
-def license_policy_check(name, target, policy, **kwargs):
-    """Checks a target against a policy.
-
-    Args:
-      name: The target.
-      target: A target to test for compliance with a policy
-      policy: A rule providing LicensePolicyInfo.
-      **kwargs: other args.
-
-    Usage:
-
-      license_policy_check(
-          name = "license_info",
-          target = ":my_app",
-          policy = "//my_org/compliance/policies:mobile_application",
-      )
-    """
-    _license_policy_check(name = name, target = target, policy = policy, **kwargs)
diff --git a/rules/license_policy_provider.bzl b/rules/license_policy_provider.bzl
deleted file mode 100644
index caecce8..0000000
--- a/rules/license_policy_provider.bzl
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright 2020 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""LicensePolicyProvider."""
-
-LicensePolicyInfo = provider(
-    doc = """Declares a policy name and the license conditions allowable under it.""",
-    fields = {
-        "conditions": "List of conditions to be met when using this software.",
-        "label": "The full path to the license policy definition.",
-        "name": "License policy name",
-    },
-)
diff --git a/rules/licenses_core.bzl b/rules/licenses_core.bzl
new file mode 100644
index 0000000..42702bd
--- /dev/null
+++ b/rules/licenses_core.bzl
@@ -0,0 +1,187 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Rules and macros for collecting LicenseInfo providers."""
+
+load("@rules_license//rules:filtered_rule_kinds.bzl", "aspect_filters")
+load("@rules_license//rules:user_filtered_rule_kinds.bzl", "user_aspect_filters")
+load(
+    "@rules_license//rules:providers.bzl",
+    "LicenseInfo",
+    "LicensedTargetInfo",
+)
+
+
+TraceInfo = provider(
+    doc = """Provides a target (as a string) to assist in debugging dependency issues.""",
+    fields = {
+        "trace": "String: a target to trace dependency edges to.",
+    },
+)
+
+def _trace_impl(ctx):
+    return TraceInfo(trace = ctx.build_setting_value)
+
+trace = rule(
+    doc = """Used to allow the specification of a target to trace while collecting license dependencies.""",
+    implementation = _trace_impl,
+    build_setting = config.string(flag = True),
+)
+
+def should_traverse(ctx, attr):
+    """Checks if the dependent attribute should be traversed.
+
+    Args:
+      ctx: The aspect evaluation context.
+      attr: The name of the attribute to be checked.
+
+    Returns:
+      True iff the attribute should be traversed.
+    """
+    k = ctx.rule.kind
+
+    for filters in [aspect_filters, user_aspect_filters]:
+        always_ignored = filters.get("*", [])
+        if k in filters:
+            attr_matches = filters[k]
+            if (attr in attr_matches or
+                "*" in attr_matches or
+                ("_*" in attr_matches and attr.startswith("_")) or
+                attr in always_ignored):
+                return False
+
+            for m in attr_matches:
+                if attr == m:
+                    return False
+
+    return True
+
+def _get_transitive_licenses(ctx, trans_licenses, trans_deps, traces, provider, filter_func):
+    attrs = [a for a in dir(ctx.rule.attr)]
+    for name in attrs:
+        if not filter_func(ctx, name):
+            continue
+        a = getattr(ctx.rule.attr, name)
+
+        # Make anything singleton into a list for convenience.
+        if type(a) != type([]):
+            a = [a]
+        for dep in a:
+            # Ignore anything that isn't a target
+            if type(dep) != "Target":
+                continue
+
+            # Targets can also include things like input files that won't have the
+            # aspect, so we additionally check for the aspect rather than assume
+            # it's on all targets.  Even some regular targets may be synthetic and
+            # not have the aspect. This provides protection against those outlier
+            # cases.
+            if provider in dep:
+                info = dep[provider]
+                if info.licenses:
+                    trans_licenses.append(info.licenses)
+                if info.deps:
+                    trans_deps.append(info.deps)
+                if info.traces:
+                    for trace in info.traces:
+                        traces.append("(" + ", ".join([str(ctx.label), ctx.rule.kind, name]) + ") -> " + trace)
+
+def gather_licenses_info_common(target, ctx, provider_factory, namespaces, filter_func):
+    """Collect license info from myself and my deps.
+
+    Any single target might directly depend on a license, or depend on
+    something that transitively depends on a license, or neither.
+    This aspect bundles all those into a single provider. At each level, we add
+    in new direct license deps found and forward up the transitive information
+    collected so far.
+
+    This is a common abstraction for crawling the dependency graph. It is parameterized
+    to allow specifying the provider that is populated with results. It is
+    configurable to select only licenses matching a certain namespace. It is also
+    configurable to specify which dependency edges should not be traced for the
+    purpose of tracing the graph.
+
+    Args:
+      target: The target of the aspect.
+      ctx: The aspect evaluation context.
+      provider_factory: abstracts the provider returned by this aspect
+      namespaces: a list of namespaces licenses must match to be included
+      filter_func: a function that returns true iff the dep edge should be ignored
+
+    Returns:
+      provider of parameterized type
+    """
+
+    # First we gather my direct license attachments
+    licenses = []
+    if ctx.rule.kind == "_license":
+        # Don't try to gather licenses from the license rule itself. We'll just
+        # blunder into the text file of the license and pick up the default
+        # attribute of the package, which we don't want.
+        pass
+    else:
+        if hasattr(ctx.rule.attr, "applicable_licenses"):
+            for dep in ctx.rule.attr.applicable_licenses:
+                if LicenseInfo in dep:
+                    lic = dep[LicenseInfo]
+
+                    # This check shouldn't be necessary since any license created
+                    # by the official code will have this set. However, one of the
+                    # tests has its own implementation of license that had to be fixed
+                    # so this is just a conservative safety check.
+                    if hasattr(lic, "namespace"):
+                        if lic.namespace in namespaces:
+                            licenses.append(lic)
+                    else:
+                        fail("should have a namespace")
+
+
+    # Now gather transitive collection of providers from the targets
+    # this target depends upon.
+    trans_licenses = []
+    trans_deps = []
+    traces = []
+    _get_transitive_licenses(ctx, trans_licenses, trans_deps, traces, provider_factory, filter_func)
+
+    if not licenses and not trans_licenses:
+        return [provider_factory(deps = depset(), licenses = depset(), traces = [])]
+
+    # If this is the target, start the sequence of traces.
+    if ctx.attr._trace[TraceInfo].trace and ctx.attr._trace[TraceInfo].trace in str(ctx.label):
+        traces = [ctx.attr._trace[TraceInfo].trace]
+
+    # Trim the number of traces accumulated since the output can be quite large.
+    # A few representative traces are generally sufficient to identify why a dependency
+    # is incorrectly incorporated.
+    if len(traces) > 10:
+        traces = traces[0:10]
+
+    if licenses:
+        # At this point we have a target and a list of directly used licenses.
+        # Bundle those together so we can report the exact targets that cause the
+        # dependency on each license. Since a list cannot be stored in a
+        # depset, even inside a provider, the list is concatenated into a
+        # string and will be unconcatenated in the output phase.
+        direct_license_uses = [LicensedTargetInfo(
+            target_under_license = target.label,
+            licenses = ",".join([str(x.label) for x in licenses]),
+        )]
+    else:
+        direct_license_uses = None
+
+    return [provider_factory(
+        target_under_license = target.label,
+        licenses = depset(tuple(licenses), transitive = trans_licenses),
+        deps = depset(direct = direct_license_uses, transitive = trans_deps),
+        traces = traces,
+    )]
diff --git a/rules/providers.bzl b/rules/providers.bzl
index 9e830ce..8778fd7 100644
--- a/rules/providers.bzl
+++ b/rules/providers.bzl
@@ -1,4 +1,4 @@
-# Copyright 2020 Google LLC
+# Copyright 2022 Google LLC
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -11,34 +11,51 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
 """Providers for license rules."""
 
 LicenseKindInfo = provider(
-    doc = """Provides information about a license kind.""",
+    doc = """Provides information about a license_kind instance.""",
     fields = {
-        "conditions": "List of conditions to be met when using this software.",
-        "label": "The full path to the license kind definition.",
-        "name": "License Name",
+        "conditions": "list(string): List of conditions to be met when using this packages under this license.",
+        "label": "Label: The full path to the license kind definition.",
+        "long_name": "string: Human readable license name",
+        "name": "string: Canonical license name",
     },
 )
 
 LicenseInfo = provider(
-    doc = """Provides information about an instance of a license.""",
+    doc = """Provides information about a license instance.""",
     fields = {
-        "copyright_notice": "Human readable short copyright notice",
-        "license_kinds": "License kinds",
-        "license_text": "License file",
-        "package_name": "Human readable package name",
+        "copyright_notice": "string: Human readable short copyright notice",
+        "label": "Label: label of the license rule",
+        "license_kinds": "list(LicenseKindInfo): License kinds ",
+        "license_text": "string: The license file path",
+        "namespace": "string: namespace of the license rule",
+        # TODO(aiuto): move to PackageInfo
+        "package_name": "string: Human readable package name",
         "package_url": "URL from which this package was downloaded.",
         "package_version": "Human readable version string",
-        "rule": "From whence this came",
     },
 )
 
-LicensesInfo = provider(
-    doc = """The set of license instances used in a target.""",
+LicensedTargetInfo = provider(
+    doc = """Lists the licenses directly used by a single target.""",
     fields = {
-        "licenses": "list(LicenseInfo).",
+        "target_under_license": "Label: The target label",
+        "licenses": "list(label of a license rule)",
     },
 )
+
+def licenses_info():
+    return provider(
+        doc = """The transitive set of licenses used by a target.""",
+        fields = {
+            "target_under_license": "Label: The top level target label.",
+            "deps": "depset(LicensedTargetInfo): The transitive list of dependencies that have licenses.",
+            "licenses": "depset(LicenseInfo)",
+            "traces": "list(string) - diagnostic for tracing a dependency relationship to a target.",
+        },
+    )
+
+# This provider is used by the aspect that is used by manifest() rules.
+TransitiveLicensesInfo = licenses_info()
diff --git a/rules/user_filtered_rule_kinds.bzl b/rules/user_filtered_rule_kinds.bzl
new file mode 100644
index 0000000..a099794
--- /dev/null
+++ b/rules/user_filtered_rule_kinds.bzl
@@ -0,0 +1,28 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Filtered rule kinds for aspect inspection.
+
+The format of this dictionary is:
+  rule_name: [attr, attr, ...]
+
+Filters for rules that are not part of the Bazel distribution should be added
+to this file.
+
+Attributes are either the explicit list of attributes to filter, or '_*' which
+would ignore all attributes prefixed with a _.
+"""
+
+# Rule kinds with attributes the aspect currently needs to ignore
+user_aspect_filters = {
+}
diff --git a/tests/BUILD b/tests/BUILD
index 256386a..10b8560 100644
--- a/tests/BUILD
+++ b/tests/BUILD
@@ -1,13 +1,17 @@
-"""Test cases for license rules."""
+# Test cases for license rules.
 
-load("@rules_license//rules:compliance.bzl", "check_license")
+load("@rules_license//tools:test_helpers.bzl", "golden_test")
+load("@rules_license//rules:compliance.bzl", "check_license", "licenses_used")
 load("@rules_license//rules:license.bzl", "license")
 load("@rules_license//rules:license_kind.bzl", "license_kind")
-load("@rules_license//tools:test_helpers.bzl", "golden_test")
 
-package(default_applicable_licenses = [":license"])
-
-licenses(["notice"])
+package(
+    default_applicable_licenses = [":license"],
+    default_visibility = [
+        "//examples:__subpackages__",
+        "//tests:__subpackages__",
+    ],
+)
 
 # license_kind rules generally appear in a central location per workspace. They
 # are intermingled with normal target build rules
@@ -41,8 +45,16 @@
 license(
     name = "license_for_extra_feature",
     package_name = "A test case package",
-    license = "LICENSE.extra",
     license_kinds = [":generic_restricted_license"],
+    license_text = "LICENSE.extra",
+)
+
+# This license is not in the "compliance" namespace and
+# therefore should not show up in the report verified by
+# :verify_cc_app_test
+license(
+    name = "internal_non_compliance_license",
+    namespace = "test_namespace",
 )
 
 cc_binary(
@@ -55,7 +67,18 @@
 
 cc_library(
     name = "c_bar",
-    srcs = ["bar.cc"],
+    srcs = [
+        "bar.cc",
+    ],
+    applicable_licenses = [
+        ":license",
+        ":license_for_extra_feature",
+        ":internal_non_compliance_license",
+    ],
+    #deps = [
+    #    "@rules_license//rules/tests/legacy:another_library_with_legacy_license_clause",
+    #    "@rules_license//rules/tests/legacy:library_with_legacy_license_clause",
+    #],
 )
 
 java_binary(
@@ -87,6 +110,28 @@
     ],
 )
 
+licenses_used(
+    name = "hello_licenses",
+    out = "hello_licenses.json",
+    deps = [":hello"],
+)
+
+py_test(
+    name = "hello_licenses_test",
+    srcs = ["hello_licenses_test.py"],
+    data = [":hello_licenses.json"],
+    python_version = "PY3",
+    deps = [
+        ":license_test_utils",
+    ],
+)
+
+py_library(
+    name = "license_test_utils",
+    srcs = ["license_test_utils.py"],
+    srcs_version = "PY3",
+)
+
 check_license(
     name = "check_java_app",
     check_conditions = False,
@@ -109,3 +154,7 @@
     golden = "hello_java_copyrights.golden",
     subject = ":hello_java_copyrights.txt",
 )
+
+exports_files([
+    "hello_licenses.golden",
+])
diff --git a/tests/apps/BUILD b/tests/apps/BUILD
new file mode 100644
index 0000000..2c0778f
--- /dev/null
+++ b/tests/apps/BUILD
@@ -0,0 +1,70 @@
+# Test cases for license rules: Sample app
+
+load("@rules_license//rules:compliance.bzl", "licenses_used")
+
+package(default_visibility = ["//examples:__subpackages__"])
+
+# Note that the app explicitly depends only on a library and some legacy
+# style licensed code.
+cc_binary(
+    name = "an_app",
+    srcs = ["an_app.cc"],
+    deps = [
+        ":level4",
+        # "@rules_license//rules/tests/legacy:another_library_with_legacy_license_clause",
+        # "@rules_license//rules/tests/legacy:library_with_legacy_license_clause",
+    ],
+)
+
+# pointless chain of libraries to show transitive rule gathering, culminating
+# in a diamond dependency on a library under license.
+# Note that the lowest level depends on some third party code
+[
+    genrule(
+        name = "level_%d_src" % level,
+        outs = ["level_%d.cc" % level],
+        # Note to reviewers: This should use string format, but format
+        # is broken when
+        cmd = """cat >$@ <<END
+            #include <iostream>
+            extern void {lower}();
+            void lib_level_{level}() {{
+                std::cout << "This is level {level}" << std::endl;
+                {lower}();
+                }}
+END
+            """.format(
+            level = level,
+            lower = "lib_level_%d" % (level - 1) if level > 0 else "new_lib_func",
+        ),
+    )
+    for level in range(5)
+]
+
+[
+    cc_library(
+        name = "level%d" % level,
+        srcs = [":level_%d.cc" % level],
+        deps = [
+            (":level%d" % (level - 1) if level > 0 else "@rules_license//tests/thrdparty:new_style_lib"),
+        ],
+    )
+    for level in range(5)
+]
+
+licenses_used(
+    name = "an_app_licenses",
+    out = "an_app_licenses.json",
+    deps = [":an_app"],
+)
+
+# Examining the golden file shows that we depend on both kinds of license.
+py_test(
+    name = "an_app_licenses_test",
+    srcs = ["an_app_licenses_test.py"],
+    data = [":an_app_licenses.json"],
+    python_version = "PY3",
+    deps = [
+        "@rules_license//tests:license_test_utils",
+    ],
+)
diff --git a/tests/apps/an_app.cc b/tests/apps/an_app.cc
new file mode 100644
index 0000000..410f986
--- /dev/null
+++ b/tests/apps/an_app.cc
@@ -0,0 +1,11 @@
+#include <iostream>
+#include <ostream>
+
+extern const char* server_message;
+extern void lib_level_4();
+
+int main(int argc, char* argv[]) {
+  std::cout << "main" << std::endl;
+  lib_level_4();
+  return 0;
+}
diff --git a/tests/apps/an_app_licenses_test.py b/tests/apps/an_app_licenses_test.py
new file mode 100644
index 0000000..9899e6c
--- /dev/null
+++ b/tests/apps/an_app_licenses_test.py
@@ -0,0 +1,30 @@
+"""Tests for google3.tools.build_defs.license.tests.apps.an_app_licenses."""
+
+import os
+
+import unittest
+from tests import license_test_utils
+
+
+class AnAppLicensesTest(unittest.TestCase):
+
+  def test_has_expected_licenses(self):
+    package_base = license_test_utils.LICENSE_PACKAGE_BASE
+    licenses_info = license_test_utils.load_licenses_info(
+        os.path.join(os.path.dirname(__file__), "an_app_licenses.json"))
+    licenses_info = license_test_utils.filter_dependencies(
+        licenses_info,
+        target_filter=lambda targ: targ.startswith(package_base),
+        licenses_filter=lambda lic: lic.startswith(package_base))
+
+    expected = {
+        "/tests/thrdparty:new_style_lib": [
+            "/tests/thrdparty:license",
+        ],
+    }
+    license_test_utils.check_licenses_of_dependencies(
+        self, licenses_info, expected)
+
+
+if __name__ == "__main__":
+  unittest.main()
diff --git a/tests/hello_cc_copyrights.golden b/tests/hello_cc_copyrights.golden
index ac28bab..38f67ad 100755
--- a/tests/hello_cc_copyrights.golden
+++ b/tests/hello_cc_copyrights.golden
@@ -1 +1,3 @@
 package(A test case package/0.0.4), copyright(Copyright © 2019 Uncle Toasty)
+package(A test case package), copyright()
+
diff --git a/tests/hello_licenses_test.py b/tests/hello_licenses_test.py
new file mode 100644
index 0000000..465688f
--- /dev/null
+++ b/tests/hello_licenses_test.py
@@ -0,0 +1,34 @@
+"""Tests for google3.tools.build_defs.license.tests.hello_licenses."""
+
+import os
+
+import unittest
+from tests import license_test_utils
+
+
+class HelloLicensesTest(unittest.TestCase):
+
+  def test_has_expected_licenses(self):
+    package_base = license_test_utils.LICENSE_PACKAGE_BASE
+    licenses_info = license_test_utils.load_licenses_info(
+        os.path.join(os.path.dirname(__file__), "hello_licenses.json"))
+    licenses_info = license_test_utils.filter_dependencies(
+        licenses_info,
+        target_filter=lambda targ: targ.startswith(package_base),
+        licenses_filter=lambda lic: lic.startswith(package_base))
+
+    expected = {
+        "/tests:hello": [
+            "/tests:license",
+        ],
+        "/tests:c_bar": [
+            "/tests:license",
+            "/tests:license_for_extra_feature",
+        ],
+    }
+    license_test_utils.check_licenses_of_dependencies(
+        self, licenses_info, expected)
+
+
+if __name__ == "__main__":
+  unittest.main()
diff --git a/tests/license_test_utils.py b/tests/license_test_utils.py
new file mode 100644
index 0000000..2c5a18a
--- /dev/null
+++ b/tests/license_test_utils.py
@@ -0,0 +1,72 @@
+"""Utilities for writing tests of license rules."""
+
+import codecs
+import json
+
+
+# This is extracted out to make it easier to keep test equivalence between
+# the OSS version and Google.
+LICENSE_PACKAGE_BASE = "/"
+
+
+def load_licenses_info(info_path):
+  """Loads the licenses_info() JSON format."""
+  with codecs.open(info_path, encoding="utf-8") as licenses_file:
+    return json.loads(licenses_file.read())
+
+
+def filter_dependencies(licenses_info, target_filter=None,
+                        licenses_filter=None):
+  """Filters licenses_info to only include dependencies of interest.
+
+  Args:
+    licenses_info: (dict) licenses info.
+    target_filter: (function): function which returns true if we should include
+        the target.
+    licenses_filter: (function): function which returns true if we should
+        include the license.
+  Returns:
+    (dict) a valid licenses_info dict.
+  """
+  top_target = licenses_info[0]
+  new_top_target = dict(top_target)
+  new_deps = []
+  for dep in top_target["dependencies"]:
+    target_name = dep["target_under_license"]
+    if target_filter and not target_filter(target_name):
+      continue
+    licenses = dep["licenses"]
+    if licenses_filter:
+      licenses = [lic for lic in licenses if licenses_filter(lic)]
+    new_deps.append({
+        "target_under_license": target_name,
+        "licenses": licenses})
+  new_top_target["dependencies"] = new_deps
+  return [new_top_target]
+
+
+def check_licenses_of_dependencies(test_case, licenses_info, expected,
+                                   path_prefix=LICENSE_PACKAGE_BASE):
+  """Checks that licenses_info contains an expected set of licenses.
+
+  Args:
+    test_case: (TestCase) the test.
+    licenses_info: (dict) licenses info.
+    expected: (dict) map of target names to the licenses they are under. Names
+        must be relative to the licenses package, not absolute.
+    path_prefix: (str) prefix to prepend to targets and licenses in expected.
+       This turns the relative target names to absolute ones.
+  """
+
+  # Turn the list of deps into a dict by target for easier comparison.
+  print(licenses_info)
+  deps_to_licenses = {
+      x["target_under_license"].lstrip('@'): set(l.strip('@') for l in x["licenses"])
+      for x in licenses_info[0]["dependencies"]}
+  print(deps_to_licenses)
+
+  for target, licenses in expected.items():
+    got_licenses = set(deps_to_licenses[path_prefix + target])
+    for lic in licenses:
+      test_case.assertIn(path_prefix + lic, got_licenses)
+  # future: Maybe check that deps is not larger than expected.
diff --git a/tests/thrdparty/BUILD b/tests/thrdparty/BUILD
new file mode 100644
index 0000000..2fa9752
--- /dev/null
+++ b/tests/thrdparty/BUILD
@@ -0,0 +1,26 @@
+# A sample library using new license rules.
+
+load("@rules_license//rules:license.bzl", "license")
+
+package(
+    default_applicable_licenses = [":license"],
+    default_visibility = [
+        "//examples:__subpackages__",
+        "//tests:__subpackages__",
+    ],
+)
+
+# The default license for an entire package is typically named "license".
+license(
+    name = "license",
+    package_name = "migrated package",
+    license_kinds = ["//licenses/generic:restricted"],
+    license_text = "LICENSE",
+)
+
+cc_library(
+    name = "new_style_lib",
+    srcs = [
+        "new_style_lib.cc",
+    ],
+)
diff --git a/tests/thrdparty/LICENSE b/tests/thrdparty/LICENSE
new file mode 100644
index 0000000..e5bcd1f
--- /dev/null
+++ b/tests/thrdparty/LICENSE
@@ -0,0 +1 @@
+I am restricted in some way.
diff --git a/tests/thrdparty/new_style_lib.cc b/tests/thrdparty/new_style_lib.cc
new file mode 100644
index 0000000..545e5b4
--- /dev/null
+++ b/tests/thrdparty/new_style_lib.cc
@@ -0,0 +1,8 @@
+
+#include <iostream>
+#include <ostream>
+
+void new_lib_func() {
+    std::cout << "This is restricted code" << std::endl;
+}
+
diff --git a/tools/checker_demo.py b/tools/checker_demo.py
index 73042aa..1075621 100644
--- a/tools/checker_demo.py
+++ b/tools/checker_demo.py
@@ -23,14 +23,19 @@
 import json
 
 # Conditions allowed for all applications
-_ALWAYS_ALLOWED_CONDITIONS = frozenset(['notice', 'permissive', 'unencumberd'])
+_ALWAYS_ALLOWED_CONDITIONS = frozenset(['notice', 'permissive', 'unencumbered'])
 
 
-def _get_licenses(licenses_info):
+def _load_license_data(licenses_info):
   with codecs.open(licenses_info, encoding='utf-8') as licenses_file:
     return json.loads(licenses_file.read())
 
 
+def unique_licenses(licenses):
+  for target in licenses:
+    for lic in target.get('licenses') or []:
+      yield lic
+
 def _do_report(out, licenses):
   """Produce a report showing the set of licenses being used.
 
@@ -41,11 +46,14 @@
   Returns:
     0 for no restricted licenses.
   """
-  for lic in licenses:  # using strange name lic because license is built-in
-    rule = lic['rule']
-    for kind in lic['license_kinds']:
-      out.write('= %s\n  kind: %s\n' % (rule, kind['target']))
-      out.write('  conditions: %s\n' % kind['conditions'])
+
+  for target in unique_licenses(licenses):
+    for lic in target.get('licenses') or []:
+      print("lic:", lic)
+      rule = lic['rule']
+      for kind in lic['license_kinds']:
+        out.write('= %s\n  kind: %s\n' % (rule, kind['target']))
+        out.write('  conditions: %s\n' % kind['conditions'])
 
 
 def _check_conditions(out, licenses, allowed_conditions):
@@ -82,11 +90,12 @@
     if l.get('package_version'):
       name =  name + "/" + l['package_version']
     # IGNORE_COPYRIGHT: Not a copyright notice. It is a variable holding one.
+    print(l)
     out.write('package(%s), copyright(%s)\n' % (name, l['copyright_notice']))
 
 
 def _do_licenses(out, licenses):
-  for lic in licenses:
+  for lic in unique_licenses(licenses):
     path = lic['license_text']
     with codecs.open(path, encoding='utf-8') as license_file:
       out.write('= %s\n' % path)
@@ -107,7 +116,14 @@
                       help='check that the dep only includes allowed license conditions')
   args = parser.parse_args()
 
-  licenses = _get_licenses(args.licenses_info)
+  license_data = _load_license_data(args.licenses_info)
+  target = license_data[0]  # we assume only one target for the demo
+
+  top_level_target = target['top_level_target']
+  dependencies = target['dependencies']
+  licenses = target['licenses']
+  print(licenses)
+
   err = 0
   with codecs.open(args.report, mode='w', encoding='utf-8') as rpt:
     _do_report(rpt, licenses)
diff --git a/tools/test_helpers.bzl b/tools/test_helpers.bzl
index 30f1980..3ffb9b7 100644
--- a/tools/test_helpers.bzl
+++ b/tools/test_helpers.bzl
@@ -38,3 +38,50 @@
             golden,
         ],
     )
+
+def golden_cmd_test(
+        name,
+        cmd,
+        golden,  # Required
+        toolchains = [],
+        tools = None,
+        exec_tools = None,
+        srcs = [],  # Optional
+        **kwargs):  # Rest
+    """Compares cmd output to golden output, passes if they are identical.
+
+    Args:
+      name: Name of the build rule.
+      cmd: The command to run to generate output.
+      golden: The golden file to be compared.
+      toolchains: List of toolchains needed to run the command, passed to genrule.
+      tools: List of tools needed to run the command, passed to genrule.
+      exec_tools: List of tools needed to run the command, passed to genrule.
+      srcs: List of sources needed as input to the command, passed to genrule.
+      **kwargs: Any additional parameters for the generated golden_test.
+    """
+    actual = name + ".output"
+
+    # There are some cases where tools are provided and exec_tools are provided.
+    # Specifying both in the same genrule, confuses the host vs exec rules,
+    # which prevents python3 from execution.
+    if tools and exec_tools:
+        fail("Only set one: tools or exec_tools.  " +
+             "Setting both confuses python execution mode (host vs exec).")
+    native.genrule(
+        name = name + "_output",
+        srcs = srcs,
+        outs = [actual],
+        cmd = cmd + " > '$@'",  # Redirect to collect output
+        toolchains = toolchains,
+        tools = tools,
+        exec_tools = exec_tools,
+        testonly = True,
+    )
+
+    golden_test(
+        name = name,
+        subject = actual,
+        golden = golden,
+        **kwargs
+    )