aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE.md16
-rw-r--r--.github/PULL_REQUEST_TEMPLATE.md7
-rw-r--r--CODEOWNERS1
-rw-r--r--LICENSE202
-rw-r--r--README.md17
-rw-r--r--WORKSPACE1
-rw-r--r--docs/CODE_OF_CONDUCT.md93
-rw-r--r--docs/CONTRIBUTING.md29
-rw-r--r--examples/BUILD1
-rw-r--r--examples/README.md16
-rw-r--r--examples/my_org/compliance/BUILD31
-rw-r--r--examples/my_org/licenses/BUILD58
-rw-r--r--examples/src/BUILD64
-rw-r--r--examples/src/mobile.cc20
-rw-r--r--examples/src/server.cc22
-rw-r--r--examples/src/server_licenses.golden28
-rw-r--r--examples/src/server_report.golden3
-rw-r--r--examples/vendor/README.md6
-rw-r--r--examples/vendor/acme/ACME_LICENSE3
-rw-r--r--examples/vendor/acme/BUILD22
-rw-r--r--examples/vendor/acme/coyote.cc17
-rw-r--r--examples/vendor/constant_gen/BUILD69
-rw-r--r--examples/vendor/constant_gen/LICENSE1
-rw-r--r--examples/vendor/constant_gen/LICENSE.on_output1
-rw-r--r--examples/vendor/constant_gen/constant_generator.py33
-rw-r--r--examples/vendor/constant_gen/defs.bzl59
-rw-r--r--examples/vendor/constant_gen/generated_code_licenses.golden15
-rw-r--r--examples/vendor/constant_gen/generator_licenses.golden15
-rw-r--r--examples/vendor/libhhgttg/BUILD25
-rw-r--r--examples/vendor/libhhgttg/LICENSE2
-rw-r--r--examples/vendor/libhhgttg/answer.cc15
-rw-r--r--rules/BUILD18
-rw-r--r--rules/compliance.bzl140
-rw-r--r--rules/default_license.bzl55
-rw-r--r--rules/gather_licenses_info.bzl126
-rw-r--r--rules/license.bzl102
-rw-r--r--rules/license_kind.bzl60
-rw-r--r--rules/license_policy.bzl53
-rw-r--r--rules/license_policy_check.bzl80
-rw-r--r--rules/license_policy_provider.bzl24
-rw-r--r--rules/providers.bzl42
-rw-r--r--tests/BUILD105
-rw-r--r--tests/Bar.java25
-rw-r--r--tests/Hello.java26
-rw-r--r--tests/LICENSE6
-rw-r--r--tests/LICENSE.extra5
-rw-r--r--tests/bar.cc19
-rw-r--r--tests/hello.cc21
-rwxr-xr-xtests/hello_cc_copyrights.golden1
-rwxr-xr-xtests/hello_java_copyrights.golden2
-rw-r--r--tools/BUILD10
-rw-r--r--tools/checker_demo.py132
-rwxr-xr-xtools/diff_test.sh36
-rw-r--r--tools/test_helpers.bzl40
54 files changed, 2020 insertions, 0 deletions
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..3c52212
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,16 @@
+## Expected Behavior
+
+
+## Actual Behavior
+
+
+## Steps to Reproduce the Problem
+
+1.
+1.
+1.
+
+## Specifications
+
+- Version:
+- Platform: \ No newline at end of file
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..a5b6657
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,7 @@
+Fixes #<issue_number_goes_here>
+
+> It's a good idea to open an issue first for discussion.
+
+- [ ] Tests pass
+- [ ] Tests and examples for any new features.
+- [ ] Appropriate changes to README are included in PR
diff --git a/CODEOWNERS b/CODEOWNERS
new file mode 100644
index 0000000..39b95d1
--- /dev/null
+++ b/CODEOWNERS
@@ -0,0 +1 @@
+* @aiuto
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+ 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/README.md b/README.md
new file mode 100644
index 0000000..7cba46e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,17 @@
+# rules_license
+
+This repository contains a set of rules and tools for
+- asserting that packages are available under specified OSS licenses
+- gathering those license assertions into artifacts to ship with code
+- applying organization specific compliance constriants against the
+ set of licenses used by a target.
+
+See [License Checking with
+Bazel](https://docs.google.com/document/d/1uwBuhAoBNrw8tmFs-NxlssI6VRolidGYdYqagLqHWt8/edit#)
+for more information about this project.
+
+WARNING: The code here is still in early, active development.
+
+## Disclaimer
+
+This is not an officially supported Google product.
diff --git a/WORKSPACE b/WORKSPACE
new file mode 100644
index 0000000..0e352d6
--- /dev/null
+++ b/WORKSPACE
@@ -0,0 +1 @@
+workspace(name = "rules_license")
diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..8529ae6
--- /dev/null
+++ b/docs/CODE_OF_CONDUCT.md
@@ -0,0 +1,93 @@
+# Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, gender identity and expression, level of
+experience, education, socio-economic status, nationality, personal appearance,
+race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, or to ban temporarily or permanently any
+contributor for other behaviors that they deem inappropriate, threatening,
+offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+This Code of Conduct also applies outside the project spaces when the Project
+Steward has a reasonable belief that an individual's behavior may have a
+negative impact on the project or its community.
+
+## Conflict Resolution
+
+We do not believe that all conflict is bad; healthy debate and disagreement
+often yield positive results. However, it is never okay to be disrespectful or
+to engage in behavior that violates the project’s code of conduct.
+
+If you see someone violating the code of conduct, you are encouraged to address
+the behavior directly with those involved. Many issues can be resolved quickly
+and easily, and this gives people more control over the outcome of their
+dispute. If you are unable to resolve the matter for any reason, or if the
+behavior is threatening or harassing, report it. We are dedicated to providing
+an environment where participants feel welcome and safe.
+
+Reports should be directed to Tony Aiuto (aiuto@google.com), the
+Project Steward(s) for bazelbuild/rules_license. It is the Project Steward’s duty to
+receive and address reported violations of the code of conduct. They will then
+work with a committee consisting of representatives from the Open Source
+Programs Office and the Google Open Source Strategy team. If for any reason you
+are uncomfortable reaching out to the Project Steward, please email
+opensource@google.com.
+
+We will investigate every complaint, but you may not receive a direct response.
+We will use our discretion in determining when and how to follow up on reported
+incidents, which may range from not taking action to permanent expulsion from
+the project and project-sponsored spaces. We will notify the accused of the
+report and provide them an opportunity to discuss it before any action is taken.
+The identity of the reporter will be omitted from the details of the report
+supplied to the accused. In potentially harmful situations, such as ongoing
+harassment or threats to anyone's safety, we may take action without notice.
+
+## Attribution
+
+This Code of Conduct is adapted from the Contributor Covenant, version 1.4,
+available at
+https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
new file mode 100644
index 0000000..22b241c
--- /dev/null
+++ b/docs/CONTRIBUTING.md
@@ -0,0 +1,29 @@
+# How to Contribute
+
+We'd love to accept your patches and contributions to this project. There are
+just a few small guidelines you need to follow.
+
+## Contributor License Agreement
+
+Contributions to this project must be accompanied by a Contributor License
+Agreement (CLA). You (or your employer) retain the copyright to your
+contribution; this simply gives us permission to use and redistribute your
+contributions as part of the project. Head over to
+<https://cla.developers.google.com/> to see your current agreements on file or
+to sign a new one.
+
+You generally only need to submit a CLA once, so if you've already submitted one
+(even if it was for a different project), you probably don't need to do it
+again.
+
+## Code reviews
+
+All submissions, including submissions by project members, require review. We
+use GitHub pull requests for this purpose. Consult
+[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
+information on using pull requests.
+
+## Community Guidelines
+
+This project follows
+[Google's Open Source Community Guidelines](https://opensource.google/conduct/).
diff --git a/examples/BUILD b/examples/BUILD
new file mode 100644
index 0000000..4d93f01
--- /dev/null
+++ b/examples/BUILD
@@ -0,0 +1 @@
+# rules_license examples
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..0afb5ca
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,16 @@
+# Examples for rules_license
+
+This set of files provides an example of how license rules can be used.
+
+Terminology
+- Organization: A company, organization or other entity that wants to use
+ license rules to enforce their particular compliance needs. These examples
+ use the work organization throughout.
+- 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
+ compliance team.
+
+## Overview
+
diff --git a/examples/my_org/compliance/BUILD b/examples/my_org/compliance/BUILD
new file mode 100644
index 0000000..074b21e
--- /dev/null
+++ b/examples/my_org/compliance/BUILD
@@ -0,0 +1,31 @@
+# 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
new file mode 100644
index 0000000..d9d5c25
--- /dev/null
+++ b/examples/my_org/licenses/BUILD
@@ -0,0 +1,58 @@
+# Example license kind definitions.
+
+# We expect that all license_kind rules used by an organization exist in a
+# central place in their source repository.
+#
+# - This allows centralized audit and, with suport from the SCM, an assurance
+# that individual developers are not adding new license kinds to the code
+# base without authorization.
+# - When third party packages are used, they might come with license rules
+# pointing to well known license_kinds from @rules_license/licenses.
+# At import time, users can mechanically transform those target paths from
+# @rules_license to their own license_kind repository.
+# - The conditions for each license_kind may be customized for the organzation's
+# needs, and not match the conditions used by the canonical versions from
+# @rules_license.
+# - In rare cases, a third_party project will define their own license_kinds.
+# There is no reasonable automatic handling of that. Organizations will have
+# to hand inspect the license text to see if it matches the conditions, and
+# then will have to hand import it to their private license_kind repository.
+
+load("@rules_license//rules:license_kind.bzl", "license_kind")
+
+package(default_visibility = ["//examples:__subpackages__"])
+
+# license_kind rules generally appear in a central location per workspace. They
+# are intermingled with normal target build rules
+license_kind(
+ name = "generic_notice",
+ conditions = [
+ "notice",
+ ],
+)
+
+license_kind(
+ name = "generic_restricted",
+ conditions = [
+ "restricted",
+ ],
+)
+
+license_kind(
+ name = "unencumbered",
+ conditions = [], # none
+)
+
+license_kind(
+ name = "lgpl_like",
+ conditions = [
+ "restricted_if_statically_linked",
+ ],
+)
+
+license_kind(
+ name = "acme_corp_paid",
+ conditions = [
+ "whitelist:acme_corp_paid",
+ ],
+)
diff --git a/examples/src/BUILD b/examples/src/BUILD
new file mode 100644
index 0000000..be415fc
--- /dev/null
+++ b/examples/src/BUILD
@@ -0,0 +1,64 @@
+# Examples of applications and interactions with licenses
+
+load("@rules_license//examples/vendor/constant_gen:defs.bzl", "constant_gen")
+load("@rules_license//rules:compliance.bzl", "check_license", "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",
+ ],
+)
+
+# Sample
+constant_gen(
+ name = "message",
+ text = "Hello, world.",
+ var = "server_message",
+)
+
+license_policy_check(
+ name = "check_server",
+ policy = "@rules_license//examples/my_org/compliance:production_service",
+ target = ":my_server",
+)
+
+cc_binary(
+ name = "my_violating_server",
+ srcs = ["server.cc"],
+ deps = [
+ ":message",
+ "@rules_license//examples/vendor/acme",
+ "@rules_license//examples/vendor/libhhgttg",
+ ],
+)
+
+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.
+
+licenses_used(
+ name = "server_licenses",
+ out = "server_licenses.json",
+ deps = [":my_server"],
+)
+
+golden_test(
+ name = "verify_server_licenses_test",
+ golden = "server_licenses.golden",
+ subject = ":server_licenses.json",
+)
diff --git a/examples/src/mobile.cc b/examples/src/mobile.cc
new file mode 100644
index 0000000..d15090f
--- /dev/null
+++ b/examples/src/mobile.cc
@@ -0,0 +1,20 @@
+// 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
new file mode 100644
index 0000000..8f7990e
--- /dev/null
+++ b/examples/src/server.cc
@@ -0,0 +1,22 @@
+// 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>
+
+extern const char* server_message;
+
+int main(int argc, char* argv[]) {
+ std::cout << server_message << std::endl;
+ return 0;
+}
diff --git a/examples/src/server_licenses.golden b/examples/src/server_licenses.golden
new file mode 100644
index 0000000..a8f388b
--- /dev/null
+++ b/examples/src/server_licenses.golden
@@ -0,0 +1,28 @@
+[
+ {
+ "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",
+ "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": "",
+ "license_text": "examples/vendor/libhhgttg/LICENSE"
+ }
+]
diff --git a/examples/src/server_report.golden b/examples/src/server_report.golden
new file mode 100644
index 0000000..6d322b1
--- /dev/null
+++ b/examples/src/server_report.golden
@@ -0,0 +1,3 @@
+= @rules_license//examples/vendor/constant_gen:license_for_emitted_code
+ kind: @@rules_license//examples/my_org/licenses:unencumbered
+ conditions: []
diff --git a/examples/vendor/README.md b/examples/vendor/README.md
new file mode 100644
index 0000000..273dea6
--- /dev/null
+++ b/examples/vendor/README.md
@@ -0,0 +1,6 @@
+# Third party packges used by your project.
+
+Note that these are presumed to be vendored in to your source tree.
+These examples to not try to address the concerns of organizations
+that wish to use license software without first examining the license
+and preserving their own copy and audit trail.
diff --git a/examples/vendor/acme/ACME_LICENSE b/examples/vendor/acme/ACME_LICENSE
new file mode 100644
index 0000000..53a5daf
--- /dev/null
+++ b/examples/vendor/acme/ACME_LICENSE
@@ -0,0 +1,3 @@
+Acme Novelty & Software provides a license to this software under terms laid
+out in specific contracts. Unless you have purchased a license from us, you may
+not use this software for any purpose.
diff --git a/examples/vendor/acme/BUILD b/examples/vendor/acme/BUILD
new file mode 100644
index 0000000..488af62
--- /dev/null
+++ b/examples/vendor/acme/BUILD
@@ -0,0 +1,22 @@
+# A package with a commercial license.
+
+load("@rules_license//rules:license.bzl", "license")
+
+package(
+ default_applicable_licenses = [":license"],
+ default_visibility = ["//visibility:public"],
+)
+
+# The default license for an entire package is typically named "license".
+license(
+ name = "license",
+ license_kinds = [
+ "@rules_license//examples/my_org/licenses:acme_corp_paid",
+ ],
+ license_text = "ACME_LICENSE",
+)
+
+cc_library(
+ name = "acme",
+ srcs = ["coyote.cc"],
+)
diff --git a/examples/vendor/acme/coyote.cc b/examples/vendor/acme/coyote.cc
new file mode 100644
index 0000000..e1e8083
--- /dev/null
+++ b/examples/vendor/acme/coyote.cc
@@ -0,0 +1,17 @@
+// 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.
+
+bool caught_road_runner() {
+ return false;
+}
diff --git a/examples/vendor/constant_gen/BUILD b/examples/vendor/constant_gen/BUILD
new file mode 100644
index 0000000..e837fd1
--- /dev/null
+++ b/examples/vendor/constant_gen/BUILD
@@ -0,0 +1,69 @@
+# An example of a code generator with a distinct license for the generated code.
+
+load(":defs.bzl", "constant_gen")
+load("@rules_license//rules:compliance.bzl", "licenses_used")
+load("@rules_license//rules:license.bzl", "license")
+load("@rules_license//tools:test_helpers.bzl", "golden_test")
+
+package(
+ default_applicable_licenses = [":license"],
+ default_visibility = ["//visibility:public"],
+)
+
+# 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",
+)
+
+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",
+ ],
+)
+
+# The generator itself will be licensed under :license
+py_binary(
+ name = "constant_generator",
+ srcs = ["constant_generator.py"],
+ python_version = "PY3",
+)
+
+# Sample: This target will be licensed under :license_for_emitted_code
+constant_gen(
+ name = "libhello",
+ text = "Hello, world.",
+ var = "hello_world",
+)
+
+# Verify the licenses are what we expect
+licenses_used(
+ name = "generator_licenses",
+ out = "generator_licenses.json",
+ deps = [":constant_generator"],
+)
+
+golden_test(
+ name = "verify_generator_licenses",
+ golden = "generator_licenses.golden",
+ subject = ":generator_licenses.json",
+)
+
+licenses_used(
+ name = "generated_code_licenses",
+ # Note: using default output file name
+ deps = [":libhello"],
+)
+
+golden_test(
+ name = "verify_generated_code_licenses",
+ golden = "generated_code_licenses.golden",
+ subject = ":generated_code_licenses.json",
+)
diff --git a/examples/vendor/constant_gen/LICENSE b/examples/vendor/constant_gen/LICENSE
new file mode 100644
index 0000000..861da0d
--- /dev/null
+++ b/examples/vendor/constant_gen/LICENSE
@@ -0,0 +1 @@
+This code is provided under a license which contains some restrictions.
diff --git a/examples/vendor/constant_gen/LICENSE.on_output b/examples/vendor/constant_gen/LICENSE.on_output
new file mode 100644
index 0000000..b699638
--- /dev/null
+++ b/examples/vendor/constant_gen/LICENSE.on_output
@@ -0,0 +1 @@
+The generated output from constant_gen has no license encumberances.
diff --git a/examples/vendor/constant_gen/constant_generator.py b/examples/vendor/constant_gen/constant_generator.py
new file mode 100644
index 0000000..6bd92b2
--- /dev/null
+++ b/examples/vendor/constant_gen/constant_generator.py
@@ -0,0 +1,33 @@
+# 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.
+
+# 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
+generates code.
+"""
+
+import sys
+
+
+def main(argv):
+ if len(argv) != 4:
+ raise Exception('usage: constant_generator out_file var_name text')
+ with open(argv[1], 'w') as out:
+ out.write('const char* %s = "%s";\n' % (argv[2], argv[3]))
+
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/examples/vendor/constant_gen/defs.bzl b/examples/vendor/constant_gen/defs.bzl
new file mode 100644
index 0000000..f979664
--- /dev/null
+++ b/examples/vendor/constant_gen/defs.bzl
@@ -0,0 +1,59 @@
+"""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]
+ ctx.actions.run(
+ mnemonic = "GenerateConstant",
+ progress_message = "Generating %s" % ctx.attr.var,
+ outputs = outputs,
+ executable = ctx.executable._generator,
+ arguments = [ctx.outputs.src_out.path, ctx.attr.var, ctx.attr.text],
+ )
+ return [DefaultInfo(files = depset(outputs))]
+
+_constant_gen = rule(
+ implementation = _constant_gen_impl,
+ attrs = {
+ "src_out": attr.output(mandatory = True),
+ "text": attr.string(mandatory = True),
+ "var": attr.string(mandatory = False),
+ "_generator": attr.label(
+ default = Label("@rules_license//examples/vendor/constant_gen:constant_generator"),
+ executable = True,
+ allow_files = True,
+ cfg = "host",
+ ),
+ },
+)
+
+def constant_gen(name, text, var):
+ # Generate the code
+ _constant_gen(
+ name = name + "_src_",
+ src_out = name + "_src_.cc",
+ text = text,
+ var = var,
+ applicable_licenses = ["@rules_license//examples/vendor/constant_gen:license_for_emitted_code"],
+ )
+
+ # And turn it into a library we can link against
+ native.cc_library(
+ name = name,
+ srcs = [name + "_src_"],
+ applicable_licenses = ["@rules_license//examples/vendor/constant_gen:license_for_emitted_code"],
+ )
diff --git a/examples/vendor/constant_gen/generated_code_licenses.golden b/examples/vendor/constant_gen/generated_code_licenses.golden
new file mode 100644
index 0000000..a37723a
--- /dev/null
+++ b/examples/vendor/constant_gen/generated_code_licenses.golden
@@ -0,0 +1,15 @@
+[
+ {
+ "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",
+ "license_text": "examples/vendor/constant_gen/LICENSE"
+ }
+]
diff --git a/examples/vendor/constant_gen/generator_licenses.golden b/examples/vendor/constant_gen/generator_licenses.golden
new file mode 100644
index 0000000..164b00b
--- /dev/null
+++ b/examples/vendor/constant_gen/generator_licenses.golden
@@ -0,0 +1,15 @@
+[
+ {
+ "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",
+ "license_text": "examples/vendor/constant_gen/LICENSE"
+ }
+]
diff --git a/examples/vendor/libhhgttg/BUILD b/examples/vendor/libhhgttg/BUILD
new file mode 100644
index 0000000..c5da389
--- /dev/null
+++ b/examples/vendor/libhhgttg/BUILD
@@ -0,0 +1,25 @@
+# A package with all code under a single license. This is the most common case
+# we expect to see.
+
+load("@rules_license//rules:license.bzl", "license")
+
+# Using a package wide default ensure that all targets are associated with the
+# license.
+package(
+ default_applicable_licenses = [":license"],
+ default_visibility = ["//visibility:public"],
+)
+
+# The default license for an entire package is typically named "license".
+license(
+ name = "license",
+ license_kinds = [
+ "@rules_license//examples/my_org/licenses:generic_notice",
+ ],
+ license_text = "LICENSE",
+)
+
+cc_library(
+ name = "libhhgttg",
+ srcs = ["answer.cc"],
+)
diff --git a/examples/vendor/libhhgttg/LICENSE b/examples/vendor/libhhgttg/LICENSE
new file mode 100644
index 0000000..660e329
--- /dev/null
+++ b/examples/vendor/libhhgttg/LICENSE
@@ -0,0 +1,2 @@
+You can do whatever you want with this software. Just incude this license
+with your distribution.
diff --git a/examples/vendor/libhhgttg/answer.cc b/examples/vendor/libhhgttg/answer.cc
new file mode 100644
index 0000000..8b78f90
--- /dev/null
+++ b/examples/vendor/libhhgttg/answer.cc
@@ -0,0 +1,15 @@
+// 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.
+
+int answer = 42;
diff --git a/rules/BUILD b/rules/BUILD
new file mode 100644
index 0000000..33f6ab6
--- /dev/null
+++ b/rules/BUILD
@@ -0,0 +1,18 @@
+# BUILD file defining @rules_license/rules
+#
+# 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.
+#
+# TODO(aiuto): Add dependency on Skylib for bzl_library so that we can later
+# generate docs with stardoc.
diff --git a/rules/compliance.bzl b/rules/compliance.bzl
new file mode 100644
index 0000000..8144003
--- /dev/null
+++ b/rules/compliance.bzl
@@ -0,0 +1,140 @@
+# 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 compliance checking."""
+
+load(
+ "@rules_license//rules:providers.bzl",
+ "LicensesInfo",
+)
+load(
+ "@rules_license//rules:gather_licenses_info.bzl",
+ "gather_licenses_info",
+ "write_licenses_info",
+)
+
+# Debugging verbosity
+_VERBOSITY = 0
+
+def _debug(loglevel, msg):
+ if _VERBOSITY > loglevel:
+ print(msg) # buildifier: disable=print
+
+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)
+
+ # Now run the checker on it
+ inputs = [licenses_file]
+ outputs = [ctx.outputs.report]
+ args = ctx.actions.args()
+ args.add("--licenses_info", licenses_file.path)
+ args.add("--report", ctx.outputs.report.path)
+ if ctx.attr.check_conditions:
+ args.add("--check_conditions")
+ if ctx.outputs.copyright_notices:
+ args.add("--copyright_notices", ctx.outputs.copyright_notices.path)
+ outputs.append(ctx.outputs.copyright_notices)
+ if ctx.outputs.license_texts:
+ args.add("--license_texts", ctx.outputs.license_texts.path)
+ outputs.append(ctx.outputs.license_texts)
+ inputs.extend(license_files)
+ ctx.actions.run(
+ mnemonic = "CheckLicenses",
+ progress_message = "Checking license compliance for %s" % ctx.label,
+ inputs = inputs,
+ outputs = outputs,
+ executable = ctx.executable._checker,
+ arguments = [args],
+ )
+ outputs.append(licenses_file) # also make the json file available.
+ return [DefaultInfo(files = depset(outputs))]
+
+_check_license = rule(
+ implementation = _check_license_impl,
+ attrs = {
+ "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(
+ default = Label("@rules_license//tools:checker_demo"),
+ executable = True,
+ allow_files = True,
+ cfg = "host",
+ ),
+ },
+)
+
+def check_license(**kwargs):
+ _check_license(**kwargs)
+
+def _licenses_used_impl(ctx):
+ """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 tmplementation method for licenses_used().""",
+ attrs = {
+ "deps": attr.label_list(
+ doc = """List of targets to collect LicenseInfo for.""",
+ aspects = [gather_licenses_info],
+ ),
+ "out": attr.output(
+ doc = """Output file.""",
+ mandatory = True,
+ ),
+ },
+)
+
+def licenses_used(name, deps, out = None, **kwargs):
+ """Collects LicensedInfo providers for a set of targets and writes as JSON.
+
+ The output is a single JSON array, with an entry for each license used.
+ See gather_licenses_info.bzl:write_licenses_info() for a description of the schema.
+
+ Args:
+ name: The target.
+ deps: A list of targets to get LicenseInfo for. The output is the union of
+ the result, not a list of information for each dependency.
+ out: The output file name. Default: <name>.json.
+ **kwargs: Other args
+
+ Usage:
+
+ licenses_used(
+ name = "license_info",
+ deps = [":my_app"],
+ out = "license_info.json",
+ )
+ """
+ if not out:
+ out = name + ".json"
+ _licenses_used(name = name, deps = deps, out = out, **kwargs)
diff --git a/rules/default_license.bzl b/rules/default_license.bzl
new file mode 100644
index 0000000..b2deeb1
--- /dev/null
+++ b/rules/default_license.bzl
@@ -0,0 +1,55 @@
+# 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",
+ "LicenseKindInfo",
+ "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 = {
+ "deps": attr.label_list(
+ mandatory = True,
+ doc = "Licenses",
+ providers = [LicenseInfo],
+ cfg = "host",
+ ),
+ "conditions": attr.string_list(
+ doc = "",
+ ),
+ },
+)
+
+def default_licenses(licenses, conditions = None):
+ _default_licenses(
+ name = "__default_licenses",
+ deps = ["%s_license" % l for l in licenses],
+ conditions = conditions,
+ )
diff --git a/rules/gather_licenses_info.bzl b/rules/gather_licenses_info.bzl
new file mode 100644
index 0000000..9d6db25
--- /dev/null
+++ b/rules/gather_licenses_info.bzl
@@ -0,0 +1,126 @@
+# 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.
+
+"""Rules and macros for collecting LicenseInfo providers."""
+
+load(
+ "@rules_license//rules:providers.bzl",
+ "LicenseInfo",
+ "LicensesInfo",
+)
+
+# Debugging verbosity
+_VERBOSITY = 0
+
+def _debug(loglevel, msg):
+ if _VERBOSITY > loglevel:
+ print(msg) # buildifier: disable=print
+
+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))]
+
+gather_licenses_info = aspect(
+ doc = """Collects LicenseInfo providers into a single LicensesInfo provider.""",
+ implementation = _gather_licenses_info_impl,
+ attr_aspects = ["applicable_licenses", "deps", "srcs"],
+)
+
+def write_licenses_info(ctx, deps, json_out):
+ """Writes LicensesInfo providers for a set of targets as JSON.
+
+ TODO(aiuto): Document JSON schema.
+
+ 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.
+
+ foo = rule(
+ implementation = _foo_impl,
+ attrs = {
+ "deps": attr.label_list(aspects = [gather_licenses_info])
+ }
+ )
+
+ def _foo_impl(ctx):
+ ...
+ out = ctx.actions.declare_file("%s_licenses.json" % ctx.label.name)
+ write_licenses_info(ctx, ctx.attr.deps, licenses_file)
+
+ Args:
+ ctx: context of the caller
+ deps: a list of deps which should have LicensesInfo 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}",
+ "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 = license.rule,
+ copyright_notice = license.copyright_notice,
+ package_name = license.package_name,
+ license_text = license.license_text.path,
+ kinds = ",\n".join(kinds),
+ ))
+ ctx.actions.write(
+ output = json_out,
+ content = "[\n%s\n]\n" % ",\n".join(licenses),
+ )
diff --git a/rules/license.bzl b/rules/license.bzl
new file mode 100644
index 0000000..2195619
--- /dev/null
+++ b/rules/license.bzl
@@ -0,0 +1,102 @@
+# 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.
+
+"""Rules for declaring the licenses used by a package."""
+
+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,
+ license_text = ctx.file.license_text,
+ rule = ctx.label,
+ )
+ _debug(0, provider)
+ return [provider]
+
+_license = rule(
+ implementation = _license_impl,
+ attrs = {
+ "license_kinds": attr.label_list(
+ mandatory = True,
+ 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 = "host",
+ ),
+ "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.",
+ ),
+ },
+)
+
+# buildifier: disable=function-docstring-args
+def license(name, license_kinds = None, license_kind = None, copyright_notice = None, package_name = None, tags = None, **kwargs):
+ """Wrapper for license rule.
+
+ Args:
+ name: str target name.
+ 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.
+ """
+ license_text_arg = kwargs.get("license_text") or "LICENSE"
+ single_kind = kwargs.get("license_kind")
+ if single_kind:
+ if license_kinds:
+ fail("Can not use both license_kind and license_kinds")
+ license_kinds = [single_kind]
+ tags = tags or []
+ _license(
+ name = name,
+ license_kinds = license_kinds,
+ license_text = license_text_arg,
+ copyright_notice = copyright_notice,
+ package_name = package_name,
+ applicable_licenses = [],
+ tags = tags,
+ )
diff --git a/rules/license_kind.bzl b/rules/license_kind.bzl
new file mode 100644
index 0000000..78b0594
--- /dev/null
+++ b/rules/license_kind.bzl
@@ -0,0 +1,60 @@
+# 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", "LicenseKindInfo")
+
+#
+# License Kind: The declaration of a well known category of license, for example,
+# Apache, MIT, LGPL v2. An organization may also declare its own license kinds
+# that it may user privately.
+#
+
+def _license_kind_impl(ctx):
+ provider = LicenseKindInfo(
+ 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_kind = rule(
+ implementation = _license_kind_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,
+ ),
+ "canonical_text": attr.label(
+ doc = "File containing the canonical text for this license. Must be UTF-8 encoded.",
+ allow_single_file = True,
+ ),
+ "url": attr.string(doc = "URL pointing to canonical license definition"),
+ },
+)
+
+def license_kind(name, **kwargs):
+ if "conditions" not in kwargs:
+ kwargs["conditions"] = []
+ _license_kind(
+ name = name,
+ applicable_licenses = [],
+ **kwargs
+ )
diff --git a/rules/license_policy.bzl b/rules/license_policy.bzl
new file mode 100644
index 0000000..0539301
--- /dev/null
+++ b/rules/license_policy.bzl
@@ -0,0 +1,53 @@
+# 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
new file mode 100644
index 0000000..bdd8c05
--- /dev/null
+++ b/rules/license_policy_check.bzl
@@ -0,0 +1,80 @@
+# 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:providers.bzl",
+ "LicensesInfo",
+)
+load(
+ "@rules_license//rules:gather_licenses_info.bzl",
+ "gather_licenses_info",
+)
+load(
+ "@rules_license//rules:license_policy_provider.bzl",
+ "LicensePolicyInfo",
+)
+
+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 tmplementation method for license_policy_check().""",
+ attrs = {
+ "target": attr.label(
+ doc = """Target to collect LicenseInfo for.""",
+ aspects = [gather_licenses_info],
+ mandatory = True,
+ allow_single_file = True,
+ ),
+ "policy": attr.label(
+ doc = """Policy definition.""",
+ mandatory = True,
+ providers = [LicensePolicyInfo],
+ ),
+ },
+)
+
+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
new file mode 100644
index 0000000..c604e69
--- /dev/null
+++ b/rules/license_policy_provider.bzl
@@ -0,0 +1,24 @@
+# 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 = {
+ "name": "License Policy Name",
+ "label": "The full path to the license policy definition.",
+ "conditions": "List of conditions to be met when using this software.",
+ },
+)
diff --git a/rules/providers.bzl b/rules/providers.bzl
new file mode 100644
index 0000000..676fc79
--- /dev/null
+++ b/rules/providers.bzl
@@ -0,0 +1,42 @@
+# 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.
+
+"""Providers for license rules."""
+
+LicenseKindInfo = provider(
+ doc = """Provides information about a license kind.""",
+ fields = {
+ "name": "License Name",
+ "label": "The full path to the license kind definition.",
+ "conditions": "List of conditions to be met when using this software.",
+ },
+)
+
+LicenseInfo = provider(
+ doc = """Provides information about an instance of a license.""",
+ fields = {
+ "copyright_notice": "Human readable short copyright notice",
+ "license_kinds": "License kinds",
+ "license_text": "License file",
+ "package_name": "Human readable package name",
+ "rule": "From whence this came",
+ },
+)
+
+LicensesInfo = provider(
+ doc = """The set of license instances used in a target.""",
+ fields = {
+ "licenses": "list(LicenseInfo).",
+ },
+)
diff --git a/tests/BUILD b/tests/BUILD
new file mode 100644
index 0000000..8bfdb19
--- /dev/null
+++ b/tests/BUILD
@@ -0,0 +1,105 @@
+# Test cases for license rules.
+
+load("@rules_license//rules:compliance.bzl", "check_license")
+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"])
+
+# license_kind rules generally appear in a central location per workspace. They
+# are intermingled with normal target build rules
+license_kind(
+ name = "generic_notice_license",
+ conditions = [
+ "notice",
+ ],
+)
+
+license_kind(
+ name = "generic_restricted_license",
+ conditions = [
+ "restricted",
+ ],
+)
+
+# The default license for an entire package is typically named "license".
+license(
+ name = "license",
+ package_name = "A test case package",
+ # Note the UTF-8 encoded copyright symbol.
+ copyright_notice = "Copyright © 2019 Uncle Toasty",
+ license_kinds = [":generic_notice_license"],
+)
+
+license(
+ name = "license_for_extra_feature",
+ package_name = "A test case package",
+ license = "LICENSE.extra",
+ license_kinds = [":generic_restricted_license"],
+)
+
+cc_binary(
+ name = "hello",
+ srcs = ["hello.cc"],
+ deps = [
+ ":c_bar",
+ ],
+)
+
+cc_library(
+ name = "c_bar",
+ srcs = ["bar.cc"],
+)
+
+java_binary(
+ name = "hello_java",
+ srcs = ["Hello.java"],
+ # Add an addition license to this target, beyond what my deps have.
+ applicable_licenses = [
+ ":license_for_extra_feature",
+ ],
+ main_class = "Hello",
+ deps = [
+ ":j_bar",
+ ],
+)
+
+java_library(
+ name = "j_bar",
+ srcs = ["Bar.java"],
+)
+
+check_license(
+ name = "check_cc_app",
+ check_conditions = False,
+ copyright_notices = "hello_cc_copyrights.txt",
+ license_texts = "hello_cc_licenses.txt",
+ report = "hello_cc_report",
+ deps = [
+ ":hello",
+ ],
+)
+
+check_license(
+ name = "check_java_app",
+ check_conditions = False,
+ copyright_notices = "hello_java_copyrights.txt",
+ license_texts = "hello_java_licenses.txt",
+ report = "hello_java_report",
+ deps = [
+ ":hello_java",
+ ],
+)
+
+golden_test(
+ name = "verify_cc_app_test",
+ golden = "hello_cc_copyrights.golden",
+ subject = ":hello_cc_copyrights.txt",
+)
+
+golden_test(
+ name = "verify_java_app_test",
+ golden = "hello_java_copyrights.golden",
+ subject = ":hello_java_copyrights.txt",
+)
diff --git a/tests/Bar.java b/tests/Bar.java
new file mode 100644
index 0000000..fd57c7d
--- /dev/null
+++ b/tests/Bar.java
@@ -0,0 +1,25 @@
+// 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.
+
+/**
+ *
+ */
+final class Bar {
+
+ static final int PI = 3;
+
+ private Bar() {
+ }
+
+}
diff --git a/tests/Hello.java b/tests/Hello.java
new file mode 100644
index 0000000..b4a1037
--- /dev/null
+++ b/tests/Hello.java
@@ -0,0 +1,26 @@
+// 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.
+
+/**
+ *
+ */
+final class Hello {
+
+ private Hello() {
+ }
+
+ public static int main(String[] args) {
+ return Bar.PI;
+ }
+}
diff --git a/tests/LICENSE b/tests/LICENSE
new file mode 100644
index 0000000..bc6bc98
--- /dev/null
+++ b/tests/LICENSE
@@ -0,0 +1,6 @@
+Copyright © 2019 Uncle Toasty
+
+Example of a notice style license.
+
+You may use this software in any way as long as you include the copyright
+notice above.
diff --git a/tests/LICENSE.extra b/tests/LICENSE.extra
new file mode 100644
index 0000000..fbcdbbb
--- /dev/null
+++ b/tests/LICENSE.extra
@@ -0,0 +1,5 @@
+Copyright (c) 2020 The authors of this software
+
+A license for an extra feature to a package.
+For example, you might have most of the package with a notice license,
+but an additional feature is added under a restricted license.
diff --git a/tests/bar.cc b/tests/bar.cc
new file mode 100644
index 0000000..1cd1214
--- /dev/null
+++ b/tests/bar.cc
@@ -0,0 +1,19 @@
+// 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.
+
+
+
+int bar() {
+ return 42;
+}
diff --git a/tests/hello.cc b/tests/hello.cc
new file mode 100644
index 0000000..8833791
--- /dev/null
+++ b/tests/hello.cc
@@ -0,0 +1,21 @@
+// 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 foo." << std::endl;
+ return 0;
+}
diff --git a/tests/hello_cc_copyrights.golden b/tests/hello_cc_copyrights.golden
new file mode 100755
index 0000000..8dc3985
--- /dev/null
+++ b/tests/hello_cc_copyrights.golden
@@ -0,0 +1 @@
+package(A test case package), copyright(Copyright © 2019 Uncle Toasty)
diff --git a/tests/hello_java_copyrights.golden b/tests/hello_java_copyrights.golden
new file mode 100755
index 0000000..9df37e5
--- /dev/null
+++ b/tests/hello_java_copyrights.golden
@@ -0,0 +1,2 @@
+package(A test case package), copyright(Copyright © 2019 Uncle Toasty)
+package(A test case package), copyright()
diff --git a/tools/BUILD b/tools/BUILD
new file mode 100644
index 0000000..d3a5658
--- /dev/null
+++ b/tools/BUILD
@@ -0,0 +1,10 @@
+# License declaration and compliance checking rules.
+
+py_binary(
+ name = "checker_demo",
+ srcs = ["checker_demo.py"],
+ python_version = "PY3",
+ visibility = ["//visibility:public"],
+)
+
+exports_files(["diff_test.sh"])
diff --git a/tools/checker_demo.py b/tools/checker_demo.py
new file mode 100644
index 0000000..0b57cee
--- /dev/null
+++ b/tools/checker_demo.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python3
+# 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 checker.
+
+This is only a demonstration. It will be replaced with other tools.
+"""
+
+import argparse
+import codecs
+import json
+
+# Conditions allowed for all applications
+_ALWAYS_ALLOWED_CONDITIONS = frozenset(['notice', 'permissive', 'unencumberd'])
+
+
+def _get_licenses(licenses_info):
+ with codecs.open(licenses_info, encoding='utf-8') as licenses_file:
+ # TODO(aiuto): Bazel is not parsing the BUILD file as utf-8, so it is
+ # double encoding characters. We should use the line below, but we have
+ # to hack up a double utf-8 decode.
+ # return json.loads(licenses_file.read())
+ b = bytearray([ord(c) for c in licenses_file.read()])
+ return json.loads(b.decode('utf-8'))
+
+
+def _do_report(out, licenses):
+ """Produce a report showing the set of licenses being used.
+
+ Args:
+ out: file object to write to
+ licenses: list of LicenseInfo objects
+
+ 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'])
+
+
+def _check_conditions(out, licenses, allowed_conditions):
+ """Check that the application does not use any disallowed licenses.
+
+ Args:
+ out: file object to write to
+ licenses: list of LicenseInfo objects
+ allowed_conditions: list of allowed condition names
+
+ Returns:
+ 0 for no licenses from outside allowed_conditions.
+ """
+ err = 0
+ for lic in licenses: # using strange name lic because license is built-in
+ rule = lic['rule']
+ for kind in lic['license_kinds']:
+ disallowed = []
+ for condition in kind['conditions']:
+ if condition not in allowed_conditions:
+ disallowed.append(condition)
+ if disallowed:
+ out.write('ERROR: %s\n' % rule)
+ out.write(' kind: %s\n' % kind['target'])
+ out.write(' conditions: %s\n' % kind['conditions'])
+ out.write(' disallowed condition: %s\n' % ','.join(disallowed))
+ err += 1
+ return err
+
+
+def _do_copyright_notices(out, licenses):
+ for l in licenses:
+ # IGNORE_COPYRIGHT: Not a copyright notice. It is a variable holding one.
+ out.write('package(%s), copyright(%s)\n' % (l.get('package_name') or '<unknown>',
+ l['copyright_notice']))
+
+
+def _do_licenses(out, licenses):
+ for lic in licenses:
+ path = lic['license_text']
+ with codecs.open(path, encoding='utf-8') as license_file:
+ out.write('= %s\n' % path)
+ out.write(license_file.read())
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description='Demonstraton license compliance checker')
+
+ parser.add_argument('--licenses_info',
+ help='path to JSON file containing all license data')
+ parser.add_argument('--report', default='report', help='Summary report')
+ parser.add_argument('--copyright_notices',
+ help='output file of all copyright notices')
+ parser.add_argument('--license_texts', help='output file of all license files')
+ parser.add_argument('--check_conditions', action='store_true',
+ help='check that the dep only includes allowed license conditions')
+ args = parser.parse_args()
+
+ licenses = _get_licenses(args.licenses_info)
+ err = 0
+ with codecs.open(args.report, mode='w', encoding='utf-8') as rpt:
+ _do_report(rpt, licenses)
+ if args.check_conditions:
+ # TODO(aiuto): Read conditions from a file of allowed conditions for
+ # a specified application deployment environment.
+ err = _check_conditions(rpt, licenses, _ALWAYS_ALLOWED_CONDITIONS)
+ if args.copyright_notices:
+ with codecs.open(
+ args.copyright_notices, mode='w', encoding='utf-8') as out:
+ _do_copyright_notices(out, licenses)
+ if args.license_texts:
+ with codecs.open(args.license_texts, mode='w', encoding='utf-8') as out:
+ _do_licenses(out, licenses)
+ return err
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/diff_test.sh b/tools/diff_test.sh
new file mode 100755
index 0000000..ee06790
--- /dev/null
+++ b/tools/diff_test.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+# 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.
+
+# Diff two files and PASS if they are equal, FAIL otherwise.
+# Usage:
+# diff_test.sh path_to_expected path_to_got
+
+# Find input files
+declare -r EXPECTED="$1"
+declare -r GOT="$2"
+
+diff_out=$(mktemp /tmp/diff_test.XXXXXXXXX)
+diff -cB "$EXPECTED" "$GOT" >"$diff_out"
+err=0
+if [[ -s "$diff_out" ]] ; then
+ cat "$diff_out"
+ err=1
+ echo 'To update:'
+ echo ' cp bazel-bin/'"$GOT" "$EXPECTED"
+ echo FAIL
+else
+ echo PASS
+fi
+exit $err
diff --git a/tools/test_helpers.bzl b/tools/test_helpers.bzl
new file mode 100644
index 0000000..30f1980
--- /dev/null
+++ b/tools/test_helpers.bzl
@@ -0,0 +1,40 @@
+# 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.
+
+"""Compare rule output to a golden version."""
+
+def golden_test(
+ name,
+ golden,
+ subject):
+ """Check that output from a rule matches the expected output.
+
+ Args:
+ name: test name
+ golden: expected content of subect
+ subject: build target
+ """
+ native.sh_test(
+ name = name,
+ size = "medium",
+ srcs = ["@rules_license//tools:diff_test.sh"],
+ args = [
+ "$(location %s)" % golden,
+ "$(location %s)" % subject,
+ ],
+ data = [
+ subject,
+ golden,
+ ],
+ )