From 8694b57ad03939fe48fde2be73576e77ba17eee4 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Fri, 5 Aug 2022 20:19:27 -0400 Subject: Add legacy licenses([notice]) clauses to the build files. This is an utterly stupid change. It is just to make it easier to import the code into Google. That, in turn, makes it easier to work towards aligning the code. --- BUILD | 4 +++- distro/BUILD | 3 ++- licenses/generic/BUILD | 12 +++++++----- licenses/spdx/BUILD | 2 ++ rules/BUILD | 2 ++ tests/BUILD | 2 ++ tools/BUILD | 2 ++ 7 files changed, 20 insertions(+), 7 deletions(-) diff --git a/BUILD b/BUILD index a763238..98caf14 100644 --- a/BUILD +++ b/BUILD @@ -19,6 +19,8 @@ package( default_visibility = ["//visibility:public"], ) +licenses(["notice"]) + license( name = "license", license_kinds = [ @@ -28,7 +30,7 @@ license( ) exports_files( - ["WORKSPACE"], + ["LICENSE", "WORKSPACE"], visibility = ["//visibility:public"], ) diff --git a/distro/BUILD b/distro/BUILD index a35a118..0231f13 100644 --- a/distro/BUILD +++ b/distro/BUILD @@ -16,12 +16,13 @@ load("//:version.bzl", "version") load("@rules_pkg//pkg:pkg.bzl", "pkg_tar") load("@rules_pkg//pkg/releasing:defs.bzl", "print_rel_notes") - package( default_visibility = ["//visibility:private"], default_applicable_licenses = ["//:license"], ) +licenses(["notice"]) + alias( name = "distro", actual = "rules_license-%s" % version, diff --git a/licenses/generic/BUILD b/licenses/generic/BUILD index ba95d52..71880cf 100644 --- a/licenses/generic/BUILD +++ b/licenses/generic/BUILD @@ -37,16 +37,18 @@ # of the well known licenses in @rules_license//licenses/spdx:* load("@rules_license//rules:license_kind.bzl", "license_kind") -filegroup( - name = "standard_package", - srcs = ["BUILD"], -) - package( default_applicable_licenses = ["//:license"], default_visibility = ["//visibility:public"], ) +licenses(["notice"]) + +filegroup( + name = "standard_package", + srcs = ["BUILD"], +) + # "none" should be used for packages which are distributed with no license of # any kind. You can use this no-op license as a positive indication that the # code's license terms were reviewed, so that linters will not flag it later as diff --git a/licenses/spdx/BUILD b/licenses/spdx/BUILD index 2ab4f35..feb0580 100644 --- a/licenses/spdx/BUILD +++ b/licenses/spdx/BUILD @@ -59,6 +59,8 @@ package( default_visibility = ["//visibility:public"], ) +licenses(["notice"]) + filegroup( name = "standard_package", srcs = ["BUILD"], diff --git a/rules/BUILD b/rules/BUILD index f0fd218..b4dde6f 100644 --- a/rules/BUILD +++ b/rules/BUILD @@ -20,6 +20,8 @@ package( default_visibility = ["//visibility:public"], ) +licenses(["notice"]) + filegroup( name = "standard_package", srcs = glob(["**"]), diff --git a/tests/BUILD b/tests/BUILD index f817cc4..256386a 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -7,6 +7,8 @@ load("@rules_license//tools:test_helpers.bzl", "golden_test") package(default_applicable_licenses = [":license"]) +licenses(["notice"]) + # license_kind rules generally appear in a central location per workspace. They # are intermingled with normal target build rules license_kind( diff --git a/tools/BUILD b/tools/BUILD index 03bbac1..9be1c2d 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -19,6 +19,8 @@ package( default_visibility = ["//visibility:public"], ) +licenses(["notice"]) + py_binary( name = "checker_demo", srcs = ["checker_demo.py"], -- cgit v1.2.3 From ae1ab6d3da82d6df8d9cafba28fc9e425793fe39 Mon Sep 17 00:00:00 2001 From: aiuto Date: Thu, 18 Aug 2022 09:45:45 -0400 Subject: Update README.md --- README.md | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index d4ce9e8..3107e1c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,24 @@ # 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 +- declaring metadata about packages, such as + - the licenses the package is available under + - the canonical package name and version + - copyright information + - ... and more TBD in the future +- gathering those license declarations into artifacts to ship with code - applying organization specific compliance constriants against the - set of licenses used by a target. + set of packages used by a target. +- (eventually) producing SBOMs for built artifacts. -See [License Checking with -Bazel](https://docs.google.com/document/d/1uwBuhAoBNrw8tmFs-NxlssI6VRolidGYdYqagLqHWt8/edit#) -for more information about the project. +WARNING: The code here is still in active initial development and will churn a lot. -WARNING: The code here is still in active initial development and may be under churn. +If you want to follow along: +- Mailing list: [bazel-ssc@googlegroups.com](https://groups.google.com/a/bazel.build/g/bazel-ssc) +- Monthly eng meeting: [calendar link](https://calendar.google.com/event?action=TEMPLATE&tmeid=MXRmdXVnMm5vZDI5bmFscHJhMjcwcm52OWlfMjAyMjA4MjJUMTYwMDAwWiBjXzUzcHBwZzFudWthZXRmb3E5NzhxaXViNmxzQGc&tmsrc=c_53pppg1nukaetfoq978qiub6ls%40group.calendar.google.com&scp=ALL) + +Background reading: +These is for learning about the problem space, and our approach to solutions. Concrete specifications will always appear in checked in code rather than documents. +- [License Checking with Bazel](https://docs.google.com/document/d/1uwBuhAoBNrw8tmFs-NxlssI6VRolidGYdYqagLqHWt8/edit#). +- [OSS Licenses and Bazel Dependency Management](https://docs.google.com/document/d/1oY53dQ0pOPEbEvIvQ3TvHcFKClkimlF9AtN89EPiVJU/edit#) +- [Adding OSS license declarations to Bazel](https://docs.google.com/document/d/1XszGbpMYNHk_FGRxKJ9IXW10KxMPdQpF5wWbZFpA4C8/edit#heading=h.5mcn15i0e1ch) -- cgit v1.2.3 From b955f4454efe7ff3460e415a3aed05eee390d651 Mon Sep 17 00:00:00 2001 From: aiuto Date: Mon, 22 Aug 2022 15:56:59 -0400 Subject: Fix calendar link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3107e1c..2260115 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ WARNING: The code here is still in active initial development and will churn a l If you want to follow along: - Mailing list: [bazel-ssc@googlegroups.com](https://groups.google.com/a/bazel.build/g/bazel-ssc) -- Monthly eng meeting: [calendar link](https://calendar.google.com/event?action=TEMPLATE&tmeid=MXRmdXVnMm5vZDI5bmFscHJhMjcwcm52OWlfMjAyMjA4MjJUMTYwMDAwWiBjXzUzcHBwZzFudWthZXRmb3E5NzhxaXViNmxzQGc&tmsrc=c_53pppg1nukaetfoq978qiub6ls%40group.calendar.google.com&scp=ALL) +- Monthly eng meeting: [calendar link](MjAyMjA4MjJUMTYwMDAwWiBjXzUzcHBwZzFudWthZXRmb3E5NzhxaXViNmxzQGc&tmsrc=c_53pppg1nukaetfoq978qiub6ls%40group.calendar.google.com&scp=ALL) Background reading: These is for learning about the problem space, and our approach to solutions. Concrete specifications will always appear in checked in code rather than documents. -- cgit v1.2.3 From 9922478bcd16cbe0cd47cc8d6cd7714beae7aa05 Mon Sep 17 00:00:00 2001 From: aiuto Date: Mon, 22 Aug 2022 21:46:25 -0400 Subject: Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2260115..2cf85fb 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ This repository contains a set of rules and tools for WARNING: The code here is still in active initial development and will churn a lot. If you want to follow along: -- Mailing list: [bazel-ssc@googlegroups.com](https://groups.google.com/a/bazel.build/g/bazel-ssc) +- Mailing list: [bazel-ssc@bazel.build](https://groups.google.com/a/bazel.build/g/bazel-ssc) - Monthly eng meeting: [calendar link](MjAyMjA4MjJUMTYwMDAwWiBjXzUzcHBwZzFudWthZXRmb3E5NzhxaXViNmxzQGc&tmsrc=c_53pppg1nukaetfoq978qiub6ls%40group.calendar.google.com&scp=ALL) Background reading: -- cgit v1.2.3 From b2fa6eab03d9a86b7a4dce728f19063ad14d1714 Mon Sep 17 00:00:00 2001 From: Mats Nilsson Date: Wed, 21 Sep 2022 11:14:59 +0200 Subject: Fix indentation and spelling mistakes This change adjust the indentation slightly to be consistent with rest of the file. Also fix a few spelling mistakes. --- rules/compliance.bzl | 2 +- rules/gather_licenses_info.bzl | 6 +++--- rules/license_policy_check.bzl | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rules/compliance.bzl b/rules/compliance.bzl index a30de9e..343759a 100644 --- a/rules/compliance.bzl +++ b/rules/compliance.bzl @@ -101,7 +101,7 @@ def _licenses_used_impl(ctx): _licenses_used = rule( implementation = _licenses_used_impl, - doc = """Internal tmplementation method for licenses_used().""", + doc = """Internal implementation method for licenses_used().""", attrs = { "deps": attr.label_list( doc = """List of targets to collect LicenseInfo for.""", diff --git a/rules/gather_licenses_info.bzl b/rules/gather_licenses_info.bzl index bd8c210..d47a821 100644 --- a/rules/gather_licenses_info.bzl +++ b/rules/gather_licenses_info.bzl @@ -58,9 +58,9 @@ gather_licenses_info = aspect( ) def _quotes_or_null(s): - if not s: - return "null" - return '"%s"' % s + if not s: + return "null" + return '"%s"' % s def write_licenses_info(ctx, deps, json_out): """Writes LicensesInfo providers for a set of targets as JSON. diff --git a/rules/license_policy_check.bzl b/rules/license_policy_check.bzl index be46913..49ab207 100644 --- a/rules/license_policy_check.bzl +++ b/rules/license_policy_check.bzl @@ -44,7 +44,7 @@ def _license_policy_check_impl(ctx): _license_policy_check = rule( implementation = _license_policy_check_impl, - doc = """Internal tmplementation method for license_policy_check().""", + doc = """Internal implementation method for license_policy_check().""", attrs = { "policy": attr.label( doc = """Policy definition.""", -- cgit v1.2.3 From d63b87cb52d8b25aac36952b49190e542e644455 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Tue, 18 Oct 2022 14:58:24 -0400 Subject: create an inital MODULE.bazel and start CI on it --- .bazelci/bzlmod_tests.yml | 33 +++++++++++++++++++++++++++++++++ .bazelci/presubmit.yml | 1 + MODULE.bazel | 12 ++++++++++++ WORKSPACE.bzlmod | 24 ++++++++++++++++++++++++ 4 files changed, 70 insertions(+) create mode 100644 .bazelci/bzlmod_tests.yml create mode 100644 MODULE.bazel create mode 100644 WORKSPACE.bzlmod diff --git a/.bazelci/bzlmod_tests.yml b/.bazelci/bzlmod_tests.yml new file mode 100644 index 0000000..124822f --- /dev/null +++ b/.bazelci/bzlmod_tests.yml @@ -0,0 +1,33 @@ +default_tests: &default_tests + test_targets: + - "//tests/... + +# +# Bazel releases +# +lts: <s + bazel: latest + +rolling: &rolling + bazel: rolling + + +# +# Commmon features by platform +# +ubuntu1804: &ubuntu + platform: ubuntu1804 + <<: *default_tests + build_targets: + - "//distro:distro" + - "//distro:relnotes" + - "//doc_build:*" + +# The cross product of bazel releases X platforms +# +tasks: + rolling_ubuntu: + name: rolling_ubuntu + test_flags: --enable_bzlmod + <<: *ubuntu + <<: *rolling diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 1f6e0d4..7e8fd83 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -1,2 +1,3 @@ imports: - tests.yml +- bzlmod_tests.yml diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 0000000..07bf06e --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,12 @@ +module( + name = "rules_license", + version = "0.0.4", # Keep in sync with version.bzl + compatibility_level = 1, +) + +# Note: rules_license must not depend on any other repositories if you are +# just using # basic rules under //rules/... + +# TODO(aiuto): Create an extension to enable the rules under //tools/... +# That will require rules_python, which we do not want to force on people who +# do not need //tools. diff --git a/WORKSPACE.bzlmod b/WORKSPACE.bzlmod new file mode 100644 index 0000000..810f646 --- /dev/null +++ b/WORKSPACE.bzlmod @@ -0,0 +1,24 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") + +maybe( + http_archive, + name = "rules_pkg", + sha256 = "451e08a4d78988c06fa3f9306ec813b836b1d076d0f055595444ba4ff22b867f", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.7.1/rules_pkg-0.7.1.tar.gz", + "https://github.com/bazelbuild/rules_pkg/releases/download/0.7.1/rules_pkg-0.7.1.tar.gz", + ], +) + +load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies") + +rules_pkg_dependencies() + +maybe( + http_archive, + name = "rules_python", + sha256 = "b593d13bb43c94ce94b483c2858e53a9b811f6f10e1e0eedc61073bd90e58d9c", + strip_prefix = "rules_python-0.12.0", + url = "https://github.com/bazelbuild/rules_python/archive/refs/tags/0.12.0.tar.gz", +) -- cgit v1.2.3 From d632f247cf370b031fd16496ed5d5f99c39d822a Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Mon, 24 Oct 2022 13:29:29 -0400 Subject: quotes --- .bazelci/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.bazelci/tests.yml b/.bazelci/tests.yml index 045e065..0b318a2 100644 --- a/.bazelci/tests.yml +++ b/.bazelci/tests.yml @@ -1,8 +1,8 @@ default_tests: &default_tests test_targets: - - "//tests/... - - "//examples/... + - "//tests/..." + - "//examples/..." # # Bazel releases -- cgit v1.2.3 From 2f17ee2d9b68ff542128bd0a1ca93087204e05a9 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Mon, 24 Oct 2022 13:32:40 -0400 Subject: one ore fix --- .bazelci/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bazelci/tests.yml b/.bazelci/tests.yml index 0b318a2..5c5a733 100644 --- a/.bazelci/tests.yml +++ b/.bazelci/tests.yml @@ -31,7 +31,7 @@ macos: &macos windows: &windows platform: windows - <<: *win_tests + <<: *default_tests # The cross product of bazel releases X platforms -- cgit v1.2.3 From e9a31de9384a37ab308172f5e76b864ebe5de6a8 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Mon, 24 Oct 2022 13:37:02 -0400 Subject: remove bad target --- .bazelci/tests.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.bazelci/tests.yml b/.bazelci/tests.yml index 5c5a733..6ae229f 100644 --- a/.bazelci/tests.yml +++ b/.bazelci/tests.yml @@ -17,13 +17,12 @@ rolling: &rolling # # Commmon features by platform # -ubuntu1804: &ubuntu - platform: ubuntu1804 +ubuntu2004: &ubuntu + platform: ubuntu2004 <<: *default_tests build_targets: - "//distro:distro" - "//distro:relnotes" - - "//doc_build:*" macos: &macos platform: macos -- cgit v1.2.3 From 695d355b4976bc8e2d1f9d0b035777d511023d25 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Mon, 24 Oct 2022 13:43:23 -0400 Subject: add lts to ci --- .bazelci/tests.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.bazelci/tests.yml b/.bazelci/tests.yml index 6ae229f..d823f34 100644 --- a/.bazelci/tests.yml +++ b/.bazelci/tests.yml @@ -36,15 +36,27 @@ windows: &windows # The cross product of bazel releases X platforms # tasks: + lts_ubuntu: + name: rolling_ubuntu + <<: *ubuntu + <<: *lts rolling_ubuntu: name: rolling_ubuntu <<: *ubuntu <<: *rolling + lts_macos: + name: rolling_macos + <<: *macos + <<: *lts rolling_macos: name: rolling_macos <<: *macos # It seems there is no rolling Bazel for macos. bazel: last_green + lts_windows: + name: rolling_windows + <<: *windows + <<: *lts rolling_windows: name: rolling_windows <<: *windows -- cgit v1.2.3 From 54c66bc0d4e82027485399c0d18f08119065fdf9 Mon Sep 17 00:00:00 2001 From: aiuto Date: Mon, 24 Oct 2022 13:45:02 -0400 Subject: Update README.md Add buildkite link --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 2cf85fb..10209d1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # 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 -- cgit v1.2.3 From e2e0b6c67a5417dd1506b5d199ffaaa1af6edd2e Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Mon, 24 Oct 2022 13:47:35 -0400 Subject: get the names right --- .bazelci/tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.bazelci/tests.yml b/.bazelci/tests.yml index d823f34..9aa764a 100644 --- a/.bazelci/tests.yml +++ b/.bazelci/tests.yml @@ -37,7 +37,7 @@ windows: &windows # tasks: lts_ubuntu: - name: rolling_ubuntu + name: lts_ubuntu <<: *ubuntu <<: *lts rolling_ubuntu: @@ -45,7 +45,7 @@ tasks: <<: *ubuntu <<: *rolling lts_macos: - name: rolling_macos + name: lts_macos <<: *macos <<: *lts rolling_macos: @@ -54,7 +54,7 @@ tasks: # It seems there is no rolling Bazel for macos. bazel: last_green lts_windows: - name: rolling_windows + name: lts_windows <<: *windows <<: *lts rolling_windows: -- cgit v1.2.3 From 84c8fe9fabbd149dff42f854c07fabbe286f93a8 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Mon, 24 Oct 2022 16:23:38 -0400 Subject: postsubmit --- .bazelci/postsubmit.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .bazelci/postsubmit.yml diff --git a/.bazelci/postsubmit.yml b/.bazelci/postsubmit.yml new file mode 100644 index 0000000..1f6e0d4 --- /dev/null +++ b/.bazelci/postsubmit.yml @@ -0,0 +1,2 @@ +imports: +- tests.yml -- cgit v1.2.3 From eb7162b1e90c59b8af04840286f6ae76d231419b Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Mon, 31 Oct 2022 21:46:37 -0400 Subject: fix.comment --- MODULE.bazel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MODULE.bazel b/MODULE.bazel index 07bf06e..80a9f62 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -5,7 +5,7 @@ module( ) # Note: rules_license must not depend on any other repositories if you are -# just using # basic rules under //rules/... +# just using basic rules under //rules/... and //licenses. # TODO(aiuto): Create an extension to enable the rules under //tools/... # That will require rules_python, which we do not want to force on people who -- cgit v1.2.3 From 801a80e42fc8dcfd0b3b901f9be2fe34f760af29 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Mon, 31 Oct 2022 21:58:44 -0400 Subject: reorder.work --- WORKSPACE.bzlmod | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/WORKSPACE.bzlmod b/WORKSPACE.bzlmod index 810f646..f2337d4 100644 --- a/WORKSPACE.bzlmod +++ b/WORKSPACE.bzlmod @@ -1,6 +1,16 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") +# This is needed for tools +maybe( + http_archive, + name = "rules_python", + sha256 = "b593d13bb43c94ce94b483c2858e53a9b811f6f10e1e0eedc61073bd90e58d9c", + strip_prefix = "rules_python-0.12.0", + url = "https://github.com/bazelbuild/rules_python/archive/refs/tags/0.12.0.tar.gz", +) + +# This is only needed to make small distributions. maybe( http_archive, name = "rules_pkg", @@ -14,11 +24,3 @@ maybe( load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies") rules_pkg_dependencies() - -maybe( - http_archive, - name = "rules_python", - sha256 = "b593d13bb43c94ce94b483c2858e53a9b811f6f10e1e0eedc61073bd90e58d9c", - strip_prefix = "rules_python-0.12.0", - url = "https://github.com/bazelbuild/rules_python/archive/refs/tags/0.12.0.tar.gz", -) -- cgit v1.2.3 From 4c79574923472a2cb3a3112456d07f6d4a168eb8 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Mon, 31 Oct 2022 22:02:36 -0400 Subject: yaml --- .bazelci/bzlmod_tests.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.bazelci/bzlmod_tests.yml b/.bazelci/bzlmod_tests.yml index 124822f..1bb4775 100644 --- a/.bazelci/bzlmod_tests.yml +++ b/.bazelci/bzlmod_tests.yml @@ -1,6 +1,7 @@ + default_tests: &default_tests test_targets: - - "//tests/... + - "//tests/..." # # Bazel releases @@ -18,6 +19,8 @@ rolling: &rolling ubuntu1804: &ubuntu platform: ubuntu1804 <<: *default_tests + test_flags: + - "--enable_bzlmod" build_targets: - "//distro:distro" - "//distro:relnotes" @@ -28,6 +31,7 @@ ubuntu1804: &ubuntu tasks: rolling_ubuntu: name: rolling_ubuntu - test_flags: --enable_bzlmod + test_flags: + - "--enable_bzlmod" <<: *ubuntu <<: *rolling -- cgit v1.2.3 From fc1c80db238cc0f6c995f96b8ad1c260a7c19b47 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Mon, 31 Oct 2022 22:32:41 -0400 Subject: add restricted license for backwards compatiblty --- licenses/generic/BUILD | 8 ++++++++ 1 file changed, 8 insertions(+) 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 @@ license_kind( ], ) +# 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", -- cgit v1.2.3 From 29224d1ef8a5cf58bb6b27747c421d3a647aef51 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Tue, 1 Nov 2022 00:01:30 -0400 Subject: Hack up a 5.x/6.x fix to the tests --- rules/gather_licenses_info.bzl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/rules/gather_licenses_info.bzl b/rules/gather_licenses_info.bzl index d47a821..9a91229 100644 --- a/rules/gather_licenses_info.bzl +++ b/rules/gather_licenses_info.bzl @@ -27,6 +27,16 @@ def _debug(loglevel, msg): if _VERBOSITY > loglevel: print(msg) # buildifier: disable=print +def _strip_null_repo(label): + """Removes the null repo name (e.g. @//) from a string. + + The is to make str(label) compatible between bazel 5.x and 6.x + """ + s = str(label) + if s.startswith('@//'): + return s[1:] + return s + def _get_transitive_licenses(deps, licenses, trans): for dep in deps: if LicenseInfo in dep: @@ -122,7 +132,7 @@ def write_licenses_info(ctx, deps, json_out): kind_conditions = kind.conditions, )) licenses.append(rule_template.format( - rule = license.rule, + rule = _strip_null_repo(license.rule), copyright_notice = license.copyright_notice, package_name = license.package_name, package_url = _quotes_or_null(license.package_url), -- cgit v1.2.3 From d8c455ebb46b0f489a4a83367e4833fda5a131db Mon Sep 17 00:00:00 2001 From: Bill Neubauer Date: Mon, 24 Oct 2022 19:52:53 -0400 Subject: Mega merge from Google The core of the PR is an expert from Google, but it applies several changes to account for Bazel differences. - deal with bazel 5.x 6.x @// handling - restore package_url and package_version. This is temporary they will move to other providers. PiperOrigin-RevId: 483521567 --- README.md | 3 - distro/BUILD | 2 +- examples/README.md | 2 +- examples/manifest/BUILD | 41 ++++ examples/manifest/android_mock.bzl | 61 +++++ examples/manifest/license_display.sh | 7 + examples/manifest/main.sh | 10 + examples/manifest/main_golden.txt | 204 ++++++++++++++++ examples/my_org/compliance/BUILD | 31 --- examples/my_org/licenses/BUILD | 13 + examples/src/BUILD | 61 +++-- examples/src/mobile.cc | 20 -- examples/src/server.cc | 4 +- examples/src/server_licenses.golden | 32 --- examples/src/server_licenses_test.py | 46 ++++ examples/src/server_report.golden | 4 +- examples/vendor/acme/BUILD | 18 +- examples/vendor/acme/coyote.cc | 3 +- examples/vendor/constant_gen/BUILD | 22 +- examples/vendor/constant_gen/constant_generator.py | 4 +- examples/vendor/constant_gen/defs.bzl | 14 -- .../constant_gen/generated_code_licenses.golden | 44 +++- .../vendor/constant_gen/generator_licenses.golden | 37 ++- examples/vendor/libhhgttg/BUILD | 18 +- examples/vendor/libhhgttg/answer.cc | 2 +- licenses/generic/BUILD | 8 + rules/BUILD | 11 + rules/check_licenses_shim.bzl | 30 +++ rules/compliance.bzl | 101 ++++++-- rules/default_license.bzl | 55 ----- rules/filtered_rule_kinds.bzl | 47 ++++ rules/gather_licenses_info.bzl | 266 +++++++++++++++------ rules/license.bzl | 110 ++++----- rules/license_impl.bzl | 82 +++++++ rules/license_kind.bzl | 15 +- rules/license_policy.bzl | 53 ---- rules/license_policy_check.bzl | 80 ------- rules/license_policy_provider.bzl | 24 -- rules/licenses_core.bzl | 187 +++++++++++++++ rules/providers.bzl | 47 ++-- rules/user_filtered_rule_kinds.bzl | 28 +++ tests/BUILD | 65 ++++- tests/apps/BUILD | 70 ++++++ tests/apps/an_app.cc | 11 + tests/apps/an_app_licenses_test.py | 30 +++ tests/hello_cc_copyrights.golden | 2 + tests/hello_licenses_test.py | 34 +++ tests/license_test_utils.py | 72 ++++++ tests/thrdparty/BUILD | 26 ++ tests/thrdparty/LICENSE | 1 + tests/thrdparty/new_style_lib.cc | 8 + tools/checker_demo.py | 34 ++- tools/test_helpers.bzl | 47 ++++ 53 files changed, 1664 insertions(+), 583 deletions(-) create mode 100644 examples/manifest/BUILD create mode 100644 examples/manifest/android_mock.bzl create mode 100644 examples/manifest/license_display.sh create mode 100755 examples/manifest/main.sh create mode 100644 examples/manifest/main_golden.txt delete mode 100644 examples/my_org/compliance/BUILD delete mode 100644 examples/src/mobile.cc delete mode 100644 examples/src/server_licenses.golden create mode 100644 examples/src/server_licenses_test.py create mode 100644 rules/check_licenses_shim.bzl delete mode 100644 rules/default_license.bzl create mode 100644 rules/filtered_rule_kinds.bzl create mode 100644 rules/license_impl.bzl delete mode 100644 rules/license_policy.bzl delete mode 100644 rules/license_policy_check.bzl delete mode 100644 rules/license_policy_provider.bzl create mode 100644 rules/licenses_core.bzl create mode 100644 rules/user_filtered_rule_kinds.bzl create mode 100644 tests/apps/BUILD create mode 100644 tests/apps/an_app.cc create mode 100644 tests/apps/an_app_licenses_test.py create mode 100644 tests/hello_licenses_test.py create mode 100644 tests/license_test_utils.py create mode 100644 tests/thrdparty/BUILD create mode 100644 tests/thrdparty/LICENSE create mode 100644 tests/thrdparty/new_style_lib.cc 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:pkg.bzl", "pkg_tar") 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 @@ Terminology - 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 @@ constant_gen( 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 @@ licenses_used( 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 - -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 +#include 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 @@ package( # 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_test( 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 @@ 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"], -) +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 @@ license_kind( ], ) +# 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 @@ package( 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 @@ def _check_license_impl(ctx): _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 @@ _check_license = rule( }, ) +# 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 @@ _licenses_used = rule( }, ) +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 d47a821..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,66 +11,113 @@ # 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 +# Definition for compliance namespace, used for filtering licenses +# based on the namespace to which they belong. +NAMESPACES = ["compliance"] -def _debug(loglevel, msg): - if _VERBOSITY > loglevel: - print(msg) # buildifier: disable=print +def _strip_null_repo(label): + """Removes the null repo name (e.g. @//) from a string. -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) + The is to make str(label) compatible between bazel 5.x and 6.x + """ + s = str(label) + if s.startswith('@//'): + return s[1:] + return s 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, @@ -86,51 +133,116 @@ def write_licenses_info(ctx, deps, json_out): 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 = 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 @@ _license = rule( 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 @@ _license = rule( " 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 @@ def _license_kind_impl(ctx): ctx.label.package, ctx.label.name, ), + long_name = ctx.attr.long_name, conditions = ctx.attr.conditions, ) return [provider] @@ -37,15 +37,16 @@ def _license_kind_impl(ctx): _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 @@ _license_kind = rule( 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( 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_binary( 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 @@ check_license( ], ) +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_test( 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 >$@ < + 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 +#include + +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 +#include + +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 codecs 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 @@ def _do_report(out, licenses): 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 @@ def _do_copyright_notices(out, licenses): 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 @@ def main(): 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 @@ def golden_test( 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 + ) -- cgit v1.2.3 From 3e56c48e13b0d77d181170b1ea67d6b52486cf11 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Wed, 2 Nov 2022 00:18:51 -0400 Subject: stop sh tests on windows for now --- .bazelci/tests.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 @@ macos: &macos windows: &windows platform: windows - <<: *default_tests + test_targets: + - "//tests/..." + - "//examples/..." + - "-//examples/manifest/..." + # The cross product of bazel releases X platforms -- cgit v1.2.3 From 73c08a684ab1029774fdc778e1bb5aa5f87393fb Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Wed, 2 Nov 2022 00:30:40 -0400 Subject: combined tests to one file --- .bazelci/bzlmod_tests.yml | 37 ------------------------------------- .bazelci/presubmit.yml | 1 - .bazelci/tests.yml | 9 +++++++++ 3 files changed, 9 insertions(+), 38 deletions(-) delete mode 100644 .bazelci/bzlmod_tests.yml diff --git a/.bazelci/bzlmod_tests.yml b/.bazelci/bzlmod_tests.yml deleted file mode 100644 index 1bb4775..0000000 --- a/.bazelci/bzlmod_tests.yml +++ /dev/null @@ -1,37 +0,0 @@ - -default_tests: &default_tests - test_targets: - - "//tests/..." - -# -# Bazel releases -# -lts: <s - bazel: latest - -rolling: &rolling - bazel: rolling - - -# -# Commmon features by platform -# -ubuntu1804: &ubuntu - platform: ubuntu1804 - <<: *default_tests - test_flags: - - "--enable_bzlmod" - build_targets: - - "//distro:distro" - - "//distro:relnotes" - - "//doc_build:*" - -# The cross product of bazel releases X platforms -# -tasks: - rolling_ubuntu: - name: rolling_ubuntu - test_flags: - - "--enable_bzlmod" - <<: *ubuntu - <<: *rolling diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 7e8fd83..1f6e0d4 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -1,3 +1,2 @@ imports: - tests.yml -- bzlmod_tests.yml diff --git a/.bazelci/tests.yml b/.bazelci/tests.yml index 812c560..153a9b4 100644 --- a/.bazelci/tests.yml +++ b/.bazelci/tests.yml @@ -65,3 +65,12 @@ tasks: name: rolling_windows <<: *windows <<: *rolling + # + # Smoke test with bzlmod + # + bzlmod_rolling_ubuntu: + name: bzlmod_rolling_ubuntu + <<: *ubuntu + <<: *rolling + build_flags: + - "--enable_bzlmod" -- cgit v1.2.3 From d7eaaa61c8223da1395fae3f8047adb9c663df55 Mon Sep 17 00:00:00 2001 From: aiuto Date: Wed, 2 Nov 2022 22:34:15 -0400 Subject: Update README.md Restore CI widget --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2cf85fb..21acefd 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # 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 -- cgit v1.2.3 From a685ce83cbd7745d9d877e38bd3770cfa7b1f481 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Wed, 2 Nov 2022 22:40:41 -0400 Subject: Bump rules_pkg dep to 0.7.1 That version is needed with newer Bazel versions because --incompatible_config_setting_private_default_visibility is now true. --- WORKSPACE | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index c4b227b..4e07726 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -25,10 +25,10 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "rules_pkg", - sha256 = "62eeb544ff1ef41d786e329e1536c1d541bb9bcad27ae984d57f18f314018e66", + sha256 = "451e08a4d78988c06fa3f9306ec813b836b1d076d0f055595444ba4ff22b867f", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.6.0/rules_pkg-0.6.0.tar.gz", - "https://github.com/bazelbuild/rules_pkg/releases/download/0.6.0/rules_pkg-0.6.0.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.7.1/rules_pkg-0.7.1.tar.gz", + "https://github.com/bazelbuild/rules_pkg/releases/download/0.7.1/rules_pkg-0.7.1.tar.gz", ], ) -- cgit v1.2.3 From 3a958d781250ce44fede289e6060d9d57705c458 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Wed, 2 Nov 2022 23:44:21 -0400 Subject: sigh --- WORKSPACE | 6 +- WORKSPACE.bzlmod | 6 +- examples/manifest/main_golden.txt | 202 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 208 insertions(+), 6 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 4e07726..2d73352 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -25,11 +25,11 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "rules_pkg", - sha256 = "451e08a4d78988c06fa3f9306ec813b836b1d076d0f055595444ba4ff22b867f", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.7.1/rules_pkg-0.7.1.tar.gz", - "https://github.com/bazelbuild/rules_pkg/releases/download/0.7.1/rules_pkg-0.7.1.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.8.0/rules_pkg-0.8.0.tar.gz", + "https://github.com/bazelbuild/rules_pkg/releases/download/0.8.0/rules_pkg-0.8.0.tar.gz", ], + sha256 = "eea0f59c28a9241156a47d7a8e32db9122f3d50b505fae0f33de6ce4d9b61834", ) load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies") diff --git a/WORKSPACE.bzlmod b/WORKSPACE.bzlmod index f2337d4..9cb942a 100644 --- a/WORKSPACE.bzlmod +++ b/WORKSPACE.bzlmod @@ -14,10 +14,10 @@ maybe( maybe( http_archive, name = "rules_pkg", - sha256 = "451e08a4d78988c06fa3f9306ec813b836b1d076d0f055595444ba4ff22b867f", + sha256 = "eea0f59c28a9241156a47d7a8e32db9122f3d50b505fae0f33de6ce4d9b61834", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.7.1/rules_pkg-0.7.1.tar.gz", - "https://github.com/bazelbuild/rules_pkg/releases/download/0.7.1/rules_pkg-0.7.1.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.8.0/rules_pkg-0.8.0.tar.gz", + "https://github.com/bazelbuild/rules_pkg/releases/download/0.8.0/rules_pkg-0.8.0.tar.gz", ], ) diff --git a/examples/manifest/main_golden.txt b/examples/manifest/main_golden.txt index 4469cce..02a32de 100644 --- a/examples/manifest/main_golden.txt +++ b/examples/manifest/main_golden.txt @@ -190,6 +190,208 @@ Licenses: 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. + + 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 -- cgit v1.2.3 From ea6c79600c331f3947582b90b9076832e9fb59af Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Thu, 3 Nov 2022 12:14:15 -0400 Subject: more workarounds for a changing bazel --- rules/gather_licenses_info.bzl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rules/gather_licenses_info.bzl b/rules/gather_licenses_info.bzl index d2d9df5..a5f1a41 100644 --- a/rules/gather_licenses_info.bzl +++ b/rules/gather_licenses_info.bzl @@ -36,6 +36,8 @@ def _strip_null_repo(label): s = str(label) if s.startswith('@//'): return s[1:] + elif s.startswith('@@//'): + return s[2:] return s def _gather_licenses_info_impl(target, ctx): -- cgit v1.2.3 From 0bc1edd4c4b1b592b9d4e3b4e38301ddd77a1913 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Tue, 22 Nov 2022 14:39:02 -0500 Subject: Add dev dependency on rules_pkg. - remove from WORKSPACE.bzlmod - build distro with bzlmod --- .bazelci/tests.yml | 3 +++ MODULE.bazel | 7 +++++-- WORKSPACE.bzlmod | 15 --------------- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/.bazelci/tests.yml b/.bazelci/tests.yml index 153a9b4..68fc0ae 100644 --- a/.bazelci/tests.yml +++ b/.bazelci/tests.yml @@ -74,3 +74,6 @@ tasks: <<: *rolling build_flags: - "--enable_bzlmod" + build_targets: + - "//distro:distro" + - "//distro:relnotes" diff --git a/MODULE.bazel b/MODULE.bazel index 80a9f62..a7d09f7 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -4,9 +4,12 @@ module( compatibility_level = 1, ) -# Note: rules_license must not depend on any other repositories if you are -# just using basic rules under //rules/... and //licenses. +# NOTE: rules_license must not depend on any other repositories if you are +# just using basic rules under //rules/... and //licenses/... # TODO(aiuto): Create an extension to enable the rules under //tools/... # That will require rules_python, which we do not want to force on people who # do not need //tools. + +# Only for development +bazel_dep(name = "rules_pkg", version = "0.7.0", dev_dependency = True) diff --git a/WORKSPACE.bzlmod b/WORKSPACE.bzlmod index 9cb942a..62e4ed8 100644 --- a/WORKSPACE.bzlmod +++ b/WORKSPACE.bzlmod @@ -9,18 +9,3 @@ maybe( strip_prefix = "rules_python-0.12.0", url = "https://github.com/bazelbuild/rules_python/archive/refs/tags/0.12.0.tar.gz", ) - -# This is only needed to make small distributions. -maybe( - http_archive, - name = "rules_pkg", - sha256 = "eea0f59c28a9241156a47d7a8e32db9122f3d50b505fae0f33de6ce4d9b61834", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.8.0/rules_pkg-0.8.0.tar.gz", - "https://github.com/bazelbuild/rules_pkg/releases/download/0.8.0/rules_pkg-0.8.0.tar.gz", - ], -) - -load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies") - -rules_pkg_dependencies() -- cgit v1.2.3 From efcc6e1a02887389c96b2322a6741677a8c99423 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Tue, 29 Nov 2022 17:29:12 -0500 Subject: Add package_info rule and a new gatherer to manage it. - Add rules/package_info.bzl - Refactor get_transitive_licenses to get_transitive_metadata. - Take a list of providers to gather. - Some hackery for Bazel 5.x support. This can be fixed if starlark visibility gets backported to Bazel 5. - Add gather_metadata.bzl. This is so we can freely experiment on techniques for multi provider support in OSS land, without impacting existing users in Google. We can merge them some day in the future. - Create a dummy sbom writer. There is also experimental code to show a different design choice for new types of Metadata. I want to preserve both for a while to have a broader design discussion over the next month. --- rules/gather_licenses_info.bzl | 4 +- rules/gather_metadata.bzl | 303 +++++++++++++++++++++++++++++++++++++++++ rules/licenses_core.bzl | 44 +++++- rules/package_info.bzl | 106 ++++++++++++++ rules/providers.bzl | 38 ++++++ rules/sbom.bzl | 159 +++++++++++++++++++++ tools/BUILD | 19 ++- tools/write_sbom.py | 117 ++++++++++++++++ 8 files changed, 777 insertions(+), 13 deletions(-) create mode 100644 rules/gather_metadata.bzl create mode 100644 rules/package_info.bzl create mode 100644 rules/sbom.bzl create mode 100644 tools/write_sbom.py diff --git a/rules/gather_licenses_info.bzl b/rules/gather_licenses_info.bzl index a5f1a41..b676972 100644 --- a/rules/gather_licenses_info.bzl +++ b/rules/gather_licenses_info.bzl @@ -16,7 +16,7 @@ load( "@rules_license//rules:licenses_core.bzl", "TraceInfo", - "gather_licenses_info_common", + "gather_metadata_info_common", "should_traverse", ) load( @@ -41,7 +41,7 @@ def _strip_null_repo(label): return s def _gather_licenses_info_impl(target, ctx): - return gather_licenses_info_common(target, ctx, TransitiveLicensesInfo, NAMESPACES, should_traverse) + return gather_metadata_info_common(target, ctx, TransitiveLicensesInfo, NAMESPACES, [], should_traverse) gather_licenses_info = aspect( doc = """Collects LicenseInfo providers into a single TransitiveLicensesInfo provider.""", diff --git a/rules/gather_metadata.bzl b/rules/gather_metadata.bzl new file mode 100644 index 0000000..4fc0bbd --- /dev/null +++ b/rules/gather_metadata.bzl @@ -0,0 +1,303 @@ +# 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:licenses_core.bzl", + "TraceInfo", + "gather_metadata_info_common", + "should_traverse", +) +load( + "@rules_license//rules:providers.bzl", + "MetadataInfo", + "PackageInfo", + "TransitiveMetadataInfo", +) + +# 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. + + The is to make str(label) compatible between bazel 5.x and 6.x + """ + s = str(label) + if s.startswith('@//'): + return s[1:] + elif s.startswith('@@//'): + return s[2:] + return s + +def _bazel_package(label): + l = _strip_null_repo(label) + return l[0:-(len(label.name) + 1)] + +def _gather_metadata_info_impl(target, ctx): + return gather_metadata_info_common(target, ctx, TransitiveMetadataInfo, NAMESPACES, [MetadataInfo, PackageInfo], should_traverse) + +gather_metadata_info = aspect( + doc = """Collects LicenseInfo providers into a single TransitiveMetadataInfo provider.""", + implementation = _gather_metadata_info_impl, + attr_aspects = ["*"], + attrs = { + "_trace": attr.label(default = "@rules_license//rules:trace_target"), + }, + provides = [TransitiveMetadataInfo], + apply_to_generating_rules = True, +) + +def _write_metadata_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 TransitiveMetadataInfo in target: + return [OutputGroupInfo(licenses = depset())] + info = target[TransitiveMetadataInfo] + 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_metadata_info.json" % ctx.label.name + content = "[\n%s\n]\n" % ",\n".join(metadata_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_metadata_info_and_write = aspect( + doc = """Collects TransitiveMetadataInfo providers and writes JSON representation to a file. + + Usage: + blaze build //some:target \ + --aspects=@rules_license//rules:gather_metadata_info.bzl%gather_metadata_info_and_write + --output_groups=licenses + """, + implementation = _write_metadata_info_impl, + attr_aspects = ["*"], + attrs = { + "_trace": attr.label(default = "@rules_license//rules:trace_target"), + }, + provides = [OutputGroupInfo], + requires = [gather_metadata_info], + apply_to_generating_rules = True, +) + +def write_metadata_info(ctx, deps, json_out): + """Writes TransitiveMetadataInfo providers for a set of targets as JSON. + + 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_metadata_info must be called from a rule implementation, where the + rule has run the gather_metadata_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_metadata_info]) + } + ) + + def _foo_impl(ctx): + ... + out = ctx.actions.declare_file("%s_licenses.json" % ctx.label.name) + write_metadata_info(ctx, ctx.attr.deps, metadata_file) + + Args: + ctx: context of the caller + deps: a list of deps which should have TransitiveMetadataInfo providers. + This requires that you have run the gather_metadata_info + aspect over them + json_out: output handle to write the JSON info + """ + licenses = [] + for dep in deps: + if TransitiveMetadataInfo in dep: + licenses.extend(metadata_info_to_json(dep[TransitiveMetadataInfo])) + ctx.actions.write( + output = json_out, + content = "[\n%s\n]\n" % ",\n".join(licenses), + ) + +def metadata_info_to_json(metadata_info): + """Render a single LicenseInfo provider to JSON + + Args: + metadata_info: A LicenseInfo. + + Returns: + [(str)] list of LicenseInfo values rendered as JSON. + """ + + main_template = """ {{ + "top_level_target": "{top_level_target}", + "dependencies": [{dependencies} + ], + "licenses": [{licenses} + ], + "packages": [{packages} + ]\n }}""" + + dep_template = """ + {{ + "target_under_license": "{target_under_license}", + "licenses": [ + {licenses} + ] + }}""" + + license_template = """ + {{ + "label": "{label}", + "bazel_package": "{bazel_package}", + "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} + }}""" + + package_info_template = """ + {{ + "target": "{label}", + "bazel_package": "{bazel_package}", + "package_name": "{package_name}", + "package_url": "{package_url}", + "package_version": "{package_version}" + }}""" + + # Build reverse map of license to user + used_by = {} + for dep in metadata_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(metadata_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), + bazel_package = _bazel_package(license.label), + used_by = ",\n ".join(sorted(['"%s"' % x for x in used_by[str(license.label)]])), + )) + + all_deps = [] + for dep in sorted(metadata_info.deps.to_list(), key = lambda x: x.target_under_license): + metadata_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])), + )) + + all_packages = [] + # We would use this if we had distinct depsets for every provider type. + #for package in sorted(metadata_info.package_info.to_list(), key = lambda x: x.label): + # all_packages.append(package_info_template.format( + # label = _strip_null_repo(package.label), + # copyright_notice = package.copyright_notice, + # package_name = package.package_name, + # package_url = package.package_url, + # package_version = package.package_version, + # )) + + for mi in sorted(metadata_info.other_metadata.to_list(), key = lambda x: x.label): + # Maybe use a map of provider class to formatter. A generic dict->json function + # in starlark would help + + # This format is for using distinct providers. I like the compile time safety. + if mi.type == "package_info": + all_packages.append(package_info_template.format( + label = _strip_null_repo(mi.label), + bazel_package = _bazel_package(mi.label), + package_name = mi.package_name, + package_url = mi.package_url, + package_version = mi.package_version, + )) + # experimental: Support the MetadataInfo bag of data + if mi.type == "package_info_alt": + all_packages.append(package_info_template.format( + label = _strip_null_repo(mi.label), + bazel_package = _bazel_package(mi.label), + # data is just a bag, so we need to use get() or "" + package_name = mi.data.get("package_name") or "", + package_url = mi.data.get("package_url") or "", + package_version = mi.data.get("package_version") or "", + )) + + return [main_template.format( + top_level_target = _strip_null_repo(metadata_info.target_under_license), + dependencies = ",".join(all_deps), + licenses = ",".join(all_licenses), + packages = ",".join(all_packages), + )] diff --git a/rules/licenses_core.bzl b/rules/licenses_core.bzl index 42702bd..cf476a4 100644 --- a/rules/licenses_core.bzl +++ b/rules/licenses_core.bzl @@ -19,6 +19,7 @@ load( "@rules_license//rules:providers.bzl", "LicenseInfo", "LicensedTargetInfo", + "TransitiveLicensesInfo", ) @@ -66,7 +67,7 @@ def should_traverse(ctx, attr): return True -def _get_transitive_licenses(ctx, trans_licenses, trans_deps, traces, provider, filter_func): +def _get_transitive_metadata(ctx, trans_licenses, trans_other_metadata, trans_package_info, 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): @@ -96,8 +97,21 @@ def _get_transitive_licenses(ctx, trans_licenses, trans_deps, traces, provider, 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. + # We only need one or the other of these stanzas. + # If we use a polymorphic approach to metadata providers, then + # this works. + if hasattr(info, "other_metadata"): + if info.other_metadata: + trans_other_metadata.append(info.other_metadata) + # But if we want more precise type safety, we would have a + # trans_* for each type of metadata. That is not user + # extensibile. + if hasattr(info, "package_info"): + if info.package_info: + trans_package_info.append(info.package_info) + +def gather_metadata_info_common(target, ctx, provider_factory, namespaces, metadata_providers, filter_func): + """Collect license and other metadata 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. @@ -116,6 +130,7 @@ def gather_licenses_info_common(target, ctx, provider_factory, namespaces, filte 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 + metadata_providers: a list of other providers of interest filter_func: a function that returns true iff the dep edge should be ignored Returns: @@ -124,6 +139,8 @@ def gather_licenses_info_common(target, ctx, provider_factory, namespaces, filte # First we gather my direct license attachments licenses = [] + other_metadata = [] + package_info = [] 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 @@ -144,14 +161,18 @@ def gather_licenses_info_common(target, ctx, provider_factory, namespaces, filte licenses.append(lic) else: fail("should have a namespace") - + for m_p in metadata_providers: + if m_p in dep: + other_metadata.append(dep[m_p]) # Now gather transitive collection of providers from the targets # this target depends upon. trans_licenses = [] + trans_other_metadata = [] + trans_package_info = [] trans_deps = [] traces = [] - _get_transitive_licenses(ctx, trans_licenses, trans_deps, traces, provider_factory, filter_func) + _get_transitive_metadata(ctx, trans_licenses, trans_other_metadata, trans_package_info, trans_deps, traces, provider_factory, filter_func) if not licenses and not trans_licenses: return [provider_factory(deps = depset(), licenses = depset(), traces = [])] @@ -179,9 +200,22 @@ def gather_licenses_info_common(target, ctx, provider_factory, namespaces, filte else: direct_license_uses = None + # This is a bit of a hack for bazel 5.x. We can not pass extra fields to + # the provider constructor, so we need to do something special for each. + # In Bazel 6.x we can use a provider initializer function that would take + # all the args and only use the ones it wants. + if provider_factory == TransitiveLicensesInfo: + 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, + )] + return [provider_factory( target_under_license = target.label, licenses = depset(tuple(licenses), transitive = trans_licenses), + other_metadata = depset(tuple(other_metadata), transitive = trans_other_metadata), deps = depset(direct = direct_license_uses, transitive = trans_deps), traces = traces, )] diff --git a/rules/package_info.bzl b/rules/package_info.bzl new file mode 100644 index 0000000..8f5460a --- /dev/null +++ b/rules/package_info.bzl @@ -0,0 +1,106 @@ +# 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 metadata about a package.""" + +load( + "@rules_license//rules:providers.bzl", + "MetadataInfo", + "PackageInfo", +) + +# +# package_info() +# + +def _package_info_impl(ctx): + provider = PackageInfo( + # Metadata providers must include a type discriminator. We don't need it + # to collect the providers, but we do need it to write the JSON. We + # key on the type field to look up the correct block of code to pull + # data out and format it. We can't to the lookup on the provider class. + type = "package_info", + label = ctx.label, + 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, + ) + # Experimental alternate design, using a generic 'data' back to hold things + generic_provider = MetadataInfo( + type = "package_info_alt", + label = ctx.label, + data = { + "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 + } + ) + return [provider, generic_provider] + +_package_info = rule( + implementation = _package_info_impl, + attrs = { + "copyright_notice": attr.string( + doc = "Copyright notice.", + ), + "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.", + ), + "package_url": attr.string( + doc = "The URL this instance of the package was download from." + + " This may be used to produce an index of OSS packages used by" + + " an applicatation.", + ), + "package_version": attr.string( + doc = "A human readable version string identifying this package." + + " This may be used to produce an index of OSS packages used" + + " by an applicatation. It should be a value that" + + " increases over time, rather than a commit hash." + ), + }, +) + +# buildifier: disable=function-docstring-args +def package_info( + name, + copyright_notice = None, + package_name = None, + package_url = None, + package_version = None, + visibility = ["//visibility:public"]): + """Wrapper for package_info rule. + + Args: + name: str target name. + 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. + 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 application. + tags: list(str) tags applied to the rule + """ + _package_info( + name = name, + copyright_notice = copyright_notice, + package_name = package_name, + package_url = package_url, + package_version = package_version, + applicable_licenses = [], + visibility = visibility, + tags = [], + testonly = 0, + ) diff --git a/rules/providers.bzl b/rules/providers.bzl index 8778fd7..b8e61ce 100644 --- a/rules/providers.bzl +++ b/rules/providers.bzl @@ -59,3 +59,41 @@ def licenses_info(): # This provider is used by the aspect that is used by manifest() rules. TransitiveLicensesInfo = licenses_info() + +# This is one way to do specify data +PackageInfo = provider( + doc = """Provides information about a package.""", + fields = { + "type": "string: How to interpret data", + "label": "Label: label of the package_info rule", + "package_name": "string: Human readable package name", + "package_url": "string: URL from which this package was downloaded.", + "package_version": "string: Human readable version string", + }, +) + +# This is more extensible. Because of the provider implementation, having a big +# dict of values rather than named fields is not much more costly. +# Design choice. Replace data with actual providers, such as PackageInfo +MetadataInfo = provider( + doc = """Generic bag of metadata.""", + fields = { + "type": "string: How to interpret data", + "label": "Label: label of the metadata rule", + "data": "String->any: Map of names to values", + } +) + +TransitiveMetadataInfo = provider( + doc = """The transitive set of licenses used by a target.""", + fields = { + "top_level_target": "Label: The top level target label.", + "other_metadata": "depset(MetatdataInfo)", + "licenses": "depset(LicenseInfo)", + "package_info": "depset(PackageInfo)", + + "target_under_license": "Label: The top level target label.", + "deps": "depset(LicensedTargetInfo): The transitive list of dependencies that have licenses.", + "traces": "list(string) - diagnostic for tracing a dependency relationship to a target.", + }, +) diff --git a/rules/sbom.bzl b/rules/sbom.bzl new file mode 100644 index 0000000..fb17adc --- /dev/null +++ b/rules/sbom.bzl @@ -0,0 +1,159 @@ +# 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. +"""SBOM generation""" + +load( + "@rules_license//rules:gather_metadata.bzl", + "gather_metadata_info", + "gather_metadata_info_and_write", + "write_metadata_info", +) +load( + "@rules_license//rules:providers.bzl", + "TransitiveLicensesInfo", +) + +# This rule is proof of concept, and may not represent the final +# form of a rule for compliance validation. +def _generate_sbom_impl(ctx): + # Gather all licenses and write information to one place + + licenses_file = ctx.actions.declare_file("_%s_licenses_info.json" % ctx.label.name) + write_metadata_info(ctx, ctx.attr.deps, licenses_file) + + license_files = [] + # if ctx.outputs.license_texts: + # license_files = get_licenses_mapping(ctx.attr.deps).keys() + + # Now turn the big blob of data into something consumable. + inputs = [licenses_file] + outputs = [ctx.outputs.out] + args = ctx.actions.args() + args.add("--licenses_info", licenses_file.path) + args.add("--out", ctx.outputs.out.path) + ctx.actions.run( + mnemonic = "CreateSBOM", + progress_message = "Creating SBOM for %s" % ctx.label, + inputs = inputs, + outputs = outputs, + executable = ctx.executable._sbom_generator, + arguments = [args], + ) + outputs.append(licenses_file) # also make the json file available. + return [DefaultInfo(files = depset(outputs))] + +_generate_sbom = rule( + implementation = _generate_sbom_impl, + attrs = { + "deps": attr.label_list( + aspects = [gather_metadata_info], + ), + "out": attr.output(mandatory = True), + "_sbom_generator": attr.label( + default = Label("@rules_license//tools:write_sbom"), + executable = True, + allow_files = True, + cfg = "exec", + ), + }, +) + +def generate_sbom(**kwargs): + _generate_sbom(**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_metadata_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 + write_metadata_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_metadata_info_and_write], + ), + "out": attr.output( + doc = """Output file.""", + mandatory = True, + ), + }, +) + +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 diff --git a/tools/BUILD b/tools/BUILD index 9be1c2d..bc4005f 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -21,6 +21,14 @@ package( licenses(["notice"]) +filegroup( + name = "standard_package", + srcs = glob(["**"]), + visibility = ["//distro:__pkg__"], +) + +exports_files(["diff_test.sh"]) + py_binary( name = "checker_demo", srcs = ["checker_demo.py"], @@ -28,10 +36,9 @@ py_binary( visibility = ["//visibility:public"], ) -exports_files(["diff_test.sh"]) - -filegroup( - name = "standard_package", - srcs = glob(["**"]), - visibility = ["//distro:__pkg__"], +py_binary( + name = "write_sbom", + srcs = ["write_sbom.py"], + python_version = "PY3", + visibility = ["//visibility:public"], ) diff --git a/tools/write_sbom.py b/tools/write_sbom.py new file mode 100644 index 0000000..18286ab --- /dev/null +++ b/tools/write_sbom.py @@ -0,0 +1,117 @@ +#!/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 datetime +import json +import os + + +TOOL = 'https//github.com/bazelbuild/rules_license/tools:write_sbom' + +def _load_package_data(package_info): + with codecs.open(package_info, encoding='utf-8') as inp: + return json.loads(inp.read()) + +def _write_sbom_header(out, package): + header = [ + 'SPDXVersion: SPDX-2.2', + 'DataLicense: CC0-1.0', + 'SPDXID: SPDXRef-DOCUMENT', + 'DocumentName: %s' % package, + # TBD + # 'DocumentNamespace: https://swinslow.net/spdx-examples/example1/hello-v3 + 'Creator: Person: %s' % os.getlogin(), + 'Creator: Tool: %s' % TOOL, + datetime.datetime.utcnow().strftime('Created: %Y-%m-%d-%H:%M:%SZ'), + '', + '##### Package: %s' % package, + ] + out.write('\n'.join(header)) + + + +def _write_sbom(out, packages): + """Produce a basic SBOM + + Args: + out: file object to write to + packages: package metadata. A big blob of JSON. + """ + for p in packages: + name = p.get('package_name') or '' + out.write('\n') + out.write('SPDXID: "%s"\n' % name) + out.write(' name: "%s"\n' % name) + if p.get('package_version'): + out.write(' versionInfo: "%s"\n' % p['package_version']) + # IGNORE_COPYRIGHT: Not a copyright notice. It is a variable holding one. + cn = p.get('copyright_notice') + if cn: + out.write(' copyrightText: "%s"\n' % cn) + kinds = p.get('license_kinds') + if kinds: + out.write(' licenseDeclared: "%s"\n' % + ','.join([k['name'] for k in kinds])) + url = p.get('package_url') + if url: + out.write(' downloadLocation: %s\n' % url) + + +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('--out', default='sbom.out', help='SBOM output') + args = parser.parse_args() + + license_data = _load_package_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'] + # It's not really packages, but this is close proxy for now + licenses = target['licenses'] + package_infos = target['packages'] + + # These are similar dicts, so merge them by package. This is not + # strictly true, as different licenese can appear in the same + # package, but it is good enough for demonstrating the sbom. + + all = {x['bazel_package']: x for x in licenses} + for pi in package_infos: + p = all.get(pi['bazel_package']) + if p: + p.update(pi) + else: + all[pi['bazel_package']] = pi + + err = 0 + with codecs.open(args.out, mode='w', encoding='utf-8') as out: + _write_sbom_header(out, package=top_level_target) + _write_sbom(out, all.values()) + return err + + +if __name__ == '__main__': + main() -- cgit v1.2.3 From 63f6183b6af32fcdccf8ab214e8a8c18af413c37 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Thu, 8 Dec 2022 16:07:44 -0500 Subject: review comments --- BUILD | 10 +++++++++- rules/gather_metadata.bzl | 3 +-- rules/package_info.bzl | 14 ++++---------- rules/providers.bzl | 4 ++-- tools/BUILD | 2 +- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/BUILD b/BUILD index 98caf14..40cfcad 100644 --- a/BUILD +++ b/BUILD @@ -13,9 +13,11 @@ # limitations under the License. load("@rules_license//rules:license.bzl", "license") +load("@rules_license//rules:package_info.bzl", "package_info") +load("@rules_license//:version.bzl", "version") package( - default_applicable_licenses = [":license"], + default_applicable_licenses = [":license", ":package_info"], default_visibility = ["//visibility:public"], ) @@ -29,6 +31,12 @@ license( license_text = "LICENSE", ) +package_info( + name = "package_info", + package_name = "rules_license", + package_version = version, +) + exports_files( ["LICENSE", "WORKSPACE"], visibility = ["//visibility:public"], diff --git a/rules/gather_metadata.bzl b/rules/gather_metadata.bzl index 4fc0bbd..9e96cba 100644 --- a/rules/gather_metadata.bzl +++ b/rules/gather_metadata.bzl @@ -101,7 +101,7 @@ gather_metadata_info_and_write = aspect( doc = """Collects TransitiveMetadataInfo providers and writes JSON representation to a file. Usage: - blaze build //some:target \ + bazel build //some:target \ --aspects=@rules_license//rules:gather_metadata_info.bzl%gather_metadata_info_and_write --output_groups=licenses """, @@ -265,7 +265,6 @@ def metadata_info_to_json(metadata_info): #for package in sorted(metadata_info.package_info.to_list(), key = lambda x: x.label): # all_packages.append(package_info_template.format( # label = _strip_null_repo(package.label), - # copyright_notice = package.copyright_notice, # package_name = package.package_name, # package_url = package.package_url, # package_version = package.package_version, diff --git a/rules/package_info.bzl b/rules/package_info.bzl index 8f5460a..a8643f8 100644 --- a/rules/package_info.bzl +++ b/rules/package_info.bzl @@ -50,9 +50,6 @@ def _package_info_impl(ctx): _package_info = rule( implementation = _package_info_impl, attrs = { - "copyright_notice": attr.string( - doc = "Copyright notice.", - ), "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" + @@ -75,7 +72,6 @@ _package_info = rule( # buildifier: disable=function-docstring-args def package_info( name, - copyright_notice = None, package_name = None, package_url = None, package_version = None, @@ -84,18 +80,16 @@ def package_info( Args: name: str target name. - 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. - 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 application. - tags: list(str) tags applied to the rule + package_url: str The canoncial URL this package distribution was retrieved from. + Note that, because of local mirroring, that might not be the + physical URL it was retrieved from. + package_version: str A human readable name identifying version of this package. """ _package_info( name = name, - copyright_notice = copyright_notice, package_name = package_name, package_url = package_url, package_version = package_version, diff --git a/rules/providers.bzl b/rules/providers.bzl index b8e61ce..3b1f090 100644 --- a/rules/providers.bzl +++ b/rules/providers.bzl @@ -87,12 +87,12 @@ MetadataInfo = provider( TransitiveMetadataInfo = provider( doc = """The transitive set of licenses used by a target.""", fields = { - "top_level_target": "Label: The top level target label.", + "top_level_target": "Label: The top level target label we are examining.", "other_metadata": "depset(MetatdataInfo)", "licenses": "depset(LicenseInfo)", "package_info": "depset(PackageInfo)", - "target_under_license": "Label: The top level target label.", + "target_under_license": "Label: A target which will be associated with some licenses.", "deps": "depset(LicensedTargetInfo): The transitive list of dependencies that have licenses.", "traces": "list(string) - diagnostic for tracing a dependency relationship to a target.", }, diff --git a/tools/BUILD b/tools/BUILD index bc4005f..2b56a34 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -15,7 +15,7 @@ """License declaration and compliance checking tools.""" package( - default_applicable_licenses = ["//:license"], + default_applicable_licenses = ["//:license", "//:package_info"], default_visibility = ["//visibility:public"], ) -- cgit v1.2.3 From 7ac78275e209ce69953248d28fa5419c3f120190 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Thu, 8 Dec 2022 16:49:47 -0500 Subject: add sbom example --- examples/sboms/BUILD | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 examples/sboms/BUILD diff --git a/examples/sboms/BUILD b/examples/sboms/BUILD new file mode 100644 index 0000000..0c31a04 --- /dev/null +++ b/examples/sboms/BUILD @@ -0,0 +1,13 @@ +# Demonstrate the generate_sbom rule + +load("@rules_license//rules:sbom.bzl", "generate_sbom") + +# There are not a lot of targets in this rule set to build a SBOM from +# so we will (in a very self-referential way) generate one for the tool +# which generates the SBOMs +# See the output in bazel-bin/examples/sboms/write_sbom.txt +generate_sbom( + name = "write_sbom_sbom", + out = "write_sbom.txt", + deps = ["//tools:write_sbom"], +) -- cgit v1.2.3 From b96be504bcaf97e59e3fc5ed1c0dec291bc0c315 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Thu, 15 Dec 2022 13:39:19 -0500 Subject: first cut at doc generation --- MODULE.bazel | 1 + WORKSPACE | 13 ++++ docs/latest.md | 179 +++++++++++++++++++++++++++++++++++++++++++++++++ rules/BUILD | 9 +++ rules/license.bzl | 18 +++-- rules/license_kind.bzl | 4 ++ rules/package_info.bzl | 10 +-- version.bzl | 2 +- 8 files changed, 224 insertions(+), 12 deletions(-) create mode 100755 docs/latest.md diff --git a/MODULE.bazel b/MODULE.bazel index a7d09f7..114446a 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -13,3 +13,4 @@ module( # Only for development bazel_dep(name = "rules_pkg", version = "0.7.0", dev_dependency = True) +bazel_dep(name = "stardoc", version = "0.5.3", dev_dependency = True) diff --git a/WORKSPACE b/WORKSPACE index 2d73352..654ea78 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -35,3 +35,16 @@ http_archive( load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies") rules_pkg_dependencies() + +http_archive( + name = "io_bazel_stardoc", + sha256 = "3fd8fec4ddec3c670bd810904e2e33170bedfe12f90adf943508184be458c8bb", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/stardoc/releases/download/0.5.3/stardoc-0.5.3.tar.gz", + "https://github.com/bazelbuild/stardoc/releases/download/0.5.3/stardoc-0.5.3.tar.gz", + ], +) + +load("@io_bazel_stardoc//:setup.bzl", "stardoc_repositories") + +stardoc_repositories() diff --git a/docs/latest.md b/docs/latest.md new file mode 100755 index 0000000..a748763 --- /dev/null +++ b/docs/latest.md @@ -0,0 +1,179 @@ + + +Rules for declaring the compliance licenses used by a package. + +See: go/license-checking-v2 + + + + +## license + +
+license(name, copyright_notice, license_kinds, license_text, namespace, package_name, package_url,
+         package_version)
+
+ + + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| copyright_notice | Copyright notice. | String | optional | "" | +| license_kinds | 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. | List of labels | optional | [] | +| license_text | The license file. | Label | optional | LICENSE | +| namespace | 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. | String | optional | "" | +| package_name | A human readable name identifying this package. This may be used to produce an index of OSS packages used by an applicatation. | String | optional | "" | +| package_url | The URL this instance of the package was download from. This may be used to produce an index of OSS packages used by an applicatation. | String | optional | "" | +| package_version | A human readable version string identifying this package. This may be used to produce an index of OSS packages used by an applicatation. It should be a value that increases over time, rather than a commit hash. | String | optional | "" | + + + + + +Proof of concept. License restriction. + + + +## license_kind + +
+license_kind(name, canonical_text, conditions, long_name, url)
+
+ + + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| canonical_text | File containing the canonical text for this license. Must be UTF-8 encoded. | Label | optional | None | +| conditions | Conditions to be met when using software under this license. Conditions are defined by the organization using this license. | List of strings | required | | +| long_name | Human readable long name of license. | String | optional | "" | +| url | URL pointing to canonical license definition | String | optional | "" | + + + + + +Rules for declaring the compliance licenses used by a package. + +See: go/license-checking-v2 + + + + + +Rules for declaring metadata about a package. + + + +## package_info + +
+package_info(name, package_name, package_url, package_version)
+
+ + + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| package_name | A human readable name identifying this package. This may be used to produce an index of OSS packages used by an applicatation. | String | optional | "" | +| package_url | The URL this instance of the package was download from. This may be used to produce an index of OSS packages used by an applicatation. | String | optional | "" | +| package_version | A human readable version string identifying this package. This may be used to produce an index of OSS packages used by an applicatation. It should be a value that increases over time, rather than a commit hash. | String | optional | "" | + + + + + +Providers for license rules. + + + +## LicenseInfo + +
+LicenseInfo(copyright_notice, label, license_kinds, license_text, namespace, package_name,
+            package_url, package_version)
+
+ +Provides information about a license instance. + +**FIELDS** + + +| Name | Description | +| :------------- | :------------- | +| 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 | +| package_name | string: Human readable package name | +| package_url | URL from which this package was downloaded. | +| package_version | Human readable version string | + + + + + +Providers for license rules. + + + +## LicenseKindInfo + +
+LicenseKindInfo(conditions, label, long_name, name)
+
+ +Provides information about a license_kind instance. + +**FIELDS** + + +| Name | Description | +| :------------- | :------------- | +| 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 | + + + + + +Providers for license rules. + + + +## PackageInfo + +
+PackageInfo(type, label, package_name, package_url, package_version)
+
+ +Provides information about a package. + +**FIELDS** + + +| Name | Description | +| :------------- | :------------- | +| type | string: How to interpret data | +| label | Label: label of the package_info rule | +| package_name | string: Human readable package name | +| package_url | string: URL from which this package was downloaded. | +| package_version | string: Human readable version string | + + + diff --git a/rules/BUILD b/rules/BUILD index 1d67059..83e8c14 100644 --- a/rules/BUILD +++ b/rules/BUILD @@ -37,3 +37,12 @@ filegroup( name = "standard_package", srcs = glob(["**"]), ) + +# Do not create a bzl_library(). That would create a dependency loop back +# to bazel-skylib. We export the .bzl files to the documentation maker. +exports_files( + glob([ + "*.bzl", + ]), + visibility = ["//doc_build:__pkg__"], +) diff --git a/rules/license.bzl b/rules/license.bzl index 183e106..b201625 100644 --- a/rules/license.bzl +++ b/rules/license.bzl @@ -74,7 +74,6 @@ _license = rule( def license( name, license_text = "LICENSE", - visibility = ["//visibility:public"], license_kind = None, license_kinds = None, copyright_notice = None, @@ -82,21 +81,27 @@ def license( package_url = None, package_version = None, namespace = "compliance", - tags = []): + tags = [], + visibility = ["//visibility:public"]): """Wrapper for license rule. + @wraps(_license) + 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. 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 application. + 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. + package_url: str The canonical URL this package was downloaded from. + package_version: str The version corresponding the the URL. + namespace: str Undocumened. Internal. tags: list(str) tags applied to the rule + visibility: list(label) visibility spec. """ if license_kind: if license_kinds: @@ -109,7 +114,6 @@ def license( 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, diff --git a/rules/license_kind.bzl b/rules/license_kind.bzl index 5c8022e..7e6c024 100644 --- a/rules/license_kind.bzl +++ b/rules/license_kind.bzl @@ -52,6 +52,10 @@ _license_kind = rule( ) def license_kind(name, **kwargs): + """Wrapper for license_kind. + + @wraps(_license_kind) + """ if "conditions" not in kwargs: kwargs["conditions"] = [] if "long_name" not in kwargs: diff --git a/rules/package_info.bzl b/rules/package_info.bzl index a8643f8..ee325ae 100644 --- a/rules/package_info.bzl +++ b/rules/package_info.bzl @@ -75,19 +75,21 @@ def package_info( package_name = None, package_url = None, package_version = None, - visibility = ["//visibility:public"]): + **kwargs): """Wrapper for package_info rule. Args: name: str target name. - 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. + 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. package_url: str The canoncial URL this package distribution was retrieved from. Note that, because of local mirroring, that might not be the physical URL it was retrieved from. package_version: str A human readable name identifying version of this package. + kwargs: other args. Most are ignored. """ + visibility = kwargs.get("visibility") or ["//visibility:public"] _package_info( name = name, package_name = package_name, diff --git a/version.bzl b/version.bzl index 0acecb6..f732579 100644 --- a/version.bzl +++ b/version.bzl @@ -13,4 +13,4 @@ # limitations under the License. """The version of rules_license.""" -version = "0.0.4" +version = "0.0.5" -- cgit v1.2.3 From 4b66bcff25ebe9ff0c67f55d3e5c251e22c59e08 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Thu, 15 Dec 2022 13:46:49 -0500 Subject: add docs link --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 21acefd..4549de1 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ WARNING: The code here is still in active initial development and will churn a l If you want to follow along: - Mailing list: [bazel-ssc@bazel.build](https://groups.google.com/a/bazel.build/g/bazel-ssc) - Monthly eng meeting: [calendar link](MjAyMjA4MjJUMTYwMDAwWiBjXzUzcHBwZzFudWthZXRmb3E5NzhxaXViNmxzQGc&tmsrc=c_53pppg1nukaetfoq978qiub6ls%40group.calendar.google.com&scp=ALL) +- [Latest docs](https://bazelbuild.github.io/rules_license/) Background reading: These is for learning about the problem space, and our approach to solutions. Concrete specifications will always appear in checked in code rather than documents. -- cgit v1.2.3 From f6a6b0d25767bc95f7104ab15464437add9b3da9 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Thu, 15 Dec 2022 13:50:13 -0500 Subject: prettify --- docs/latest.md | 2 -- rules/license.bzl | 1 - rules/license_impl.bzl | 1 - 3 files changed, 4 deletions(-) diff --git a/docs/latest.md b/docs/latest.md index a748763..031daea 100755 --- a/docs/latest.md +++ b/docs/latest.md @@ -2,7 +2,6 @@ Rules for declaring the compliance licenses used by a package. -See: go/license-checking-v2 @@ -63,7 +62,6 @@ license_kind(name, Date: Thu, 15 Dec 2022 13:51:20 -0500 Subject: linkat --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4549de1..463e0b2 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ WARNING: The code here is still in active initial development and will churn a l If you want to follow along: - Mailing list: [bazel-ssc@bazel.build](https://groups.google.com/a/bazel.build/g/bazel-ssc) - Monthly eng meeting: [calendar link](MjAyMjA4MjJUMTYwMDAwWiBjXzUzcHBwZzFudWthZXRmb3E5NzhxaXViNmxzQGc&tmsrc=c_53pppg1nukaetfoq978qiub6ls%40group.calendar.google.com&scp=ALL) -- [Latest docs](https://bazelbuild.github.io/rules_license/) +- [Latest docs](https://bazelbuild.github.io/rules_license/latest.md) Background reading: These is for learning about the problem space, and our approach to solutions. Concrete specifications will always appear in checked in code rather than documents. -- cgit v1.2.3 From 1a3c59aa20e04f78a5eff4b91f84845a8cca377a Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Thu, 15 Dec 2022 13:53:51 -0500 Subject: do not bump versoin yet --- version.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.bzl b/version.bzl index f732579..0acecb6 100644 --- a/version.bzl +++ b/version.bzl @@ -13,4 +13,4 @@ # limitations under the License. """The version of rules_license.""" -version = "0.0.5" +version = "0.0.4" -- cgit v1.2.3 From 5b1abc04581905698c3994c8d80702602a7237de Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Thu, 15 Dec 2022 14:21:01 -0500 Subject: got wrapping --- docs/latest.md | 7 ------- rules/package_info.bzl | 2 ++ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/docs/latest.md b/docs/latest.md index 031daea..a7c373f 100755 --- a/docs/latest.md +++ b/docs/latest.md @@ -58,13 +58,6 @@ license_kind(name, Date: Fri, 16 Dec 2022 13:06:56 -0500 Subject: add missing docs glue --- docs/_config.yml | 1 + docs/_includes/head-custom.html | 6 ++++++ docs/_includes/head.html | 2 ++ docs/favicon.ico | Bin 0 -> 5430 bytes docs/index.md | 9 +++++++++ 5 files changed, 18 insertions(+) create mode 100644 docs/_config.yml create mode 100644 docs/_includes/head-custom.html create mode 100644 docs/_includes/head.html create mode 100755 docs/favicon.ico create mode 100644 docs/index.md diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000..c419263 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file diff --git a/docs/_includes/head-custom.html b/docs/_includes/head-custom.html new file mode 100644 index 0000000..f1e4106 --- /dev/null +++ b/docs/_includes/head-custom.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/docs/_includes/head.html b/docs/_includes/head.html new file mode 100644 index 0000000..52e6032 --- /dev/null +++ b/docs/_includes/head.html @@ -0,0 +1,2 @@ + + diff --git a/docs/favicon.ico b/docs/favicon.ico new file mode 100755 index 0000000..507f14f Binary files /dev/null and b/docs/favicon.ico differ diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..a2f7596 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,9 @@ +# bazelbuild/rules_license + +Bazel rules for defining and using package licensing and other metadata + +Use bazel-ssc@bazel.build for discussion. + +## Reference + +* [Latest Snapshot at head](latest.md) -- cgit v1.2.3 From b6abe9dc04c0ad96432975c502ea93e9c9c52b59 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Fri, 16 Dec 2022 13:12:48 -0500 Subject: wtf happened to doc_build --- doc_build/BUILD | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++ doc_build/common.md | 53 ++++++++++++++++++++++++++ doc_build/merge.py | 86 +++++++++++++++++++++++++++++++++++++++++ doc_build/toc.md.tpl | 28 ++++++++++++++ 4 files changed, 272 insertions(+) create mode 100644 doc_build/BUILD create mode 100644 doc_build/common.md create mode 100755 doc_build/merge.py create mode 100644 doc_build/toc.md.tpl diff --git a/doc_build/BUILD b/doc_build/BUILD new file mode 100644 index 0000000..5aadfde --- /dev/null +++ b/doc_build/BUILD @@ -0,0 +1,105 @@ +# Copyright 2022 The Bazel Authors. All rights reserved. +# +# 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. +"""Generate the reference documentation. + +How to: + bazel build //doc_build:reference + cp bazel-bin/doc_build/reference.md docs/latest.md + git commit -m 'update docs' docs/latest.md +""" + +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") +load("@io_bazel_stardoc//stardoc:stardoc.bzl", "stardoc") +load("@rules_python//python:defs.bzl", "py_library") +load("//:version.bzl", "version") + +package(default_package_metadata = ["//:license", "//:package_info"]) + +filegroup( + name = "standard_package", + srcs = [ + "BUILD", + ] + glob([ + "*.bzl", + "*.py", + ]), + visibility = ["//distro:__pkg__"], +) + +exports_files( + glob([ + "*.bzl", + ]), + visibility = [ + "//distro:__pkg__", + ], +) + +# pairs of rule name and the source file to get it from +# Must put macro wrapped rules after their wrapper +# buildifier: leave-alone, do not sort +ORDER = [ + ("license", "//rules:license.bzl"), + ("_license", "//rules:license.bzl"), + ("license_kind", "//rules:license_kind.bzl"), + ("_license_kind", "//rules:license_kind.bzl"), + ("package_info", "//rules:package_info.bzl"), + ("_package_info", "//rules:package_info.bzl"), + ("LicenseInfo", "//rules:providers.bzl"), + ("LicenseKindInfo", "//rules:providers.bzl"), + ("PackageInfo", "//rules:providers.bzl"), +] + +genrule( + name = "reference", + srcs = ["%s.md" % rule for rule, _ in ORDER], + outs = ["reference.md"], + cmd = "$(location :merge) $(SRCS) >$@", + exec_tools = [":merge"], +) + +[ + stardoc( + name = "%s_gen" % rule, + out = "%s.md" % rule, + input = src, + symbol_names = [ + rule, + ], + deps = [":lib_of_everything"], + ) + for rule, src in ORDER + if src +] + +# gather all rules that should be documented +bzl_library( + name = "lib_of_everything", + srcs = [ + "//:version.bzl", + "//rules:standard_package", + # "@bazel_skylib//lib:paths", + ], + visibility = ["//visibility:public"], +) + +# This is experimental. We are waiting for stardoc to get the features which +# are done in merge. +py_binary( + name = "merge", + srcs = ["merge.py"], + python_version = "PY3", + srcs_version = "PY3", + visibility = ["//visibility:private"], +) diff --git a/doc_build/common.md b/doc_build/common.md new file mode 100644 index 0000000..d2be5ed --- /dev/null +++ b/doc_build/common.md @@ -0,0 +1,53 @@ + + +### Common Attributes + +These attributes are used in several rules within this module. + +**ATTRIBUTES** + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :-------------: | :-------------: | :------------- | +| out | Name of the output file. This file will always be created and used to access the package content. If `package_file_name` is also specified, `out` will be a symlink. | String | required | | +| package_file_name | The name of the file which will contain the package. The name may contain variables in the forms `{var}` and $(var)`. The values for substitution are specified through `package_variables` or taken from [ctx.var](https://bazel.build/rules/lib/ctx#var). | String | optional | package type specific | +| package_variables | A target that provides `PackageVariablesInfo` to substitute into `package_file_name`. | Label | optional | None | +| attributes | Attributes to set on entities created within packages. Not to be confused with bazel rule attributes. See 'Mapping "Attributes"' below | Undefined. | optional | Varies. Consult individual rule documentation for details. | + +See +[examples/naming_package_files](https://github.com/bazelbuild/rules_pkg/tree/main/examples/naming_package_files) +for examples of how `out`, `package_file_name`, and `package_variables` +interact. + +@since(0.8.0): File name substitution now supports the $(var) syntax. +@since(0.8.0): File name substitution now supports direct use of [ctx.var](https://bazel.build/rules/lib/ctx#var). + + + +### Mapping "Attributes" + +The "attributes" attribute specifies properties of package contents as used in +rules such as `pkg_files`, and `pkg_mkdirs`. These allow fine-grained control +of the contents of your package. For example: + +```python +attributes = pkg_attributes( + mode = "0644", + user = "root", + group = "wheel", + my_custom_attribute = "some custom value", +) +``` + +`mode`, `user`, and `group` correspond to common UNIX-style filesystem +permissions. Attributes should always be specified using the `pkg_attributes` +helper macro. + +Each mapping rule has some default mapping attributes. At this time, the only +default is "mode", which will be set if it is not otherwise overridden by the user. + +If `user` and `group` are not specified, then defaults for them will be chosen +by the underlying package builder. Any specific behavior from package builders +should not be relied upon. + +Any other attributes should be specified as additional arguments to +`pkg_attributes`. diff --git a/doc_build/merge.py b/doc_build/merge.py new file mode 100755 index 0000000..7212f78 --- /dev/null +++ b/doc_build/merge.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +# Copyright 2022 The Bazel Authors. All rights reserved. +# +# 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. +"""merge stardoc output into a single page. + +- concatenates files +- corrects things that stardoc botches +""" + +import re +import sys +import typing + + +# I think stardoc changed the format of the id strings. Sigh. +ID_RE = re.compile(r'') +ID_OLD_RE = re.compile(r'') +WRAPS_RE = re.compile(r'@wraps\((.*)\)') +SINCE_RE = re.compile(r'@since\(([^)]*)\)') +CENTER_RE = re.compile(r'

([^<]*)

') + + +def merge_file(file: str, out, wrapper_map:typing.Dict[str, str]) -> None: + with open(file, 'r') as inp: + content = inp.read() + m = ID_RE.search(content) + if not m: + m = ID_OLD_RE.search(content) + this_rule = m.group(1) if m else None + m = WRAPS_RE.search(content) + if m: + # I wrap something, so don't emit me. + wrapper_map[m.group(1)] = this_rule + return + # If something wraps me, rewrite myself with the wrapper name. + if this_rule in wrapper_map: + content = content.replace(this_rule, wrapper_map[this_rule]) + merge_text(content, out) + + +def merge_text(text: str, out) -> None: + """Merge a block of text into an output stream. + + Args: + text: block of text produced by Starroc. + out: an output file stream. + """ + for line in text.split('\n'): + line = SINCE_RE.sub(r'
Since \1
', line) + + if line.startswith('| :'): + line = fix_stardoc_table_align(line) + # Compensate for https://github.com/bazelbuild/stardoc/issues/118. + # Convert escaped HTML
  • back to raw text + line = line.replace('<li>', '
  • ') + line = CENTER_RE.sub(r'\1', line) + _ = out.write(line) + _ = out.write('\n') + + +def fix_stardoc_table_align(line: str) -> str: + """Change centered descriptions to left justified.""" + if line.startswith('| :-------------: | :-------------: '): + return '| :------------ | :--------------- | :---------: | :---------: | :----------- |' + return line + + +def main(argv: typing.Sequence[str]) -> None: + wrapper_map = {} + for file in argv[1:]: + merge_file(file, sys.stdout, wrapper_map) + + +if __name__ == '__main__': + main(sys.argv) diff --git a/doc_build/toc.md.tpl b/doc_build/toc.md.tpl new file mode 100644 index 0000000..224f363 --- /dev/null +++ b/doc_build/toc.md.tpl @@ -0,0 +1,28 @@ +# rules_pkg - {VERSION} + + -- cgit v1.2.3 From e519ec82719f0b17c9b0b58cfe967c86ba47676f Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Wed, 21 Dec 2022 13:36:42 -0500 Subject: remove extra file --- README.md | 2 +- doc_build/common.md | 53 ----------------------------------------------------- 2 files changed, 1 insertion(+), 54 deletions(-) delete mode 100644 doc_build/common.md diff --git a/README.md b/README.md index 463e0b2..1527f74 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ WARNING: The code here is still in active initial development and will churn a l If you want to follow along: - Mailing list: [bazel-ssc@bazel.build](https://groups.google.com/a/bazel.build/g/bazel-ssc) - Monthly eng meeting: [calendar link](MjAyMjA4MjJUMTYwMDAwWiBjXzUzcHBwZzFudWthZXRmb3E5NzhxaXViNmxzQGc&tmsrc=c_53pppg1nukaetfoq978qiub6ls%40group.calendar.google.com&scp=ALL) -- [Latest docs](https://bazelbuild.github.io/rules_license/latest.md) +- [Latest docs](https://bazelbuild.github.io/rules_license/latest.html) Background reading: These is for learning about the problem space, and our approach to solutions. Concrete specifications will always appear in checked in code rather than documents. diff --git a/doc_build/common.md b/doc_build/common.md deleted file mode 100644 index d2be5ed..0000000 --- a/doc_build/common.md +++ /dev/null @@ -1,53 +0,0 @@ - - -### Common Attributes - -These attributes are used in several rules within this module. - -**ATTRIBUTES** - -| Name | Description | Type | Mandatory | Default | -| :------------- | :------------- | :-------------: | :-------------: | :------------- | -| out | Name of the output file. This file will always be created and used to access the package content. If `package_file_name` is also specified, `out` will be a symlink. | String | required | | -| package_file_name | The name of the file which will contain the package. The name may contain variables in the forms `{var}` and $(var)`. The values for substitution are specified through `package_variables` or taken from [ctx.var](https://bazel.build/rules/lib/ctx#var). | String | optional | package type specific | -| package_variables | A target that provides `PackageVariablesInfo` to substitute into `package_file_name`. | Label | optional | None | -| attributes | Attributes to set on entities created within packages. Not to be confused with bazel rule attributes. See 'Mapping "Attributes"' below | Undefined. | optional | Varies. Consult individual rule documentation for details. | - -See -[examples/naming_package_files](https://github.com/bazelbuild/rules_pkg/tree/main/examples/naming_package_files) -for examples of how `out`, `package_file_name`, and `package_variables` -interact. - -@since(0.8.0): File name substitution now supports the $(var) syntax. -@since(0.8.0): File name substitution now supports direct use of [ctx.var](https://bazel.build/rules/lib/ctx#var). - - - -### Mapping "Attributes" - -The "attributes" attribute specifies properties of package contents as used in -rules such as `pkg_files`, and `pkg_mkdirs`. These allow fine-grained control -of the contents of your package. For example: - -```python -attributes = pkg_attributes( - mode = "0644", - user = "root", - group = "wheel", - my_custom_attribute = "some custom value", -) -``` - -`mode`, `user`, and `group` correspond to common UNIX-style filesystem -permissions. Attributes should always be specified using the `pkg_attributes` -helper macro. - -Each mapping rule has some default mapping attributes. At this time, the only -default is "mode", which will be set if it is not otherwise overridden by the user. - -If `user` and `group` are not specified, then defaults for them will be chosen -by the underlying package builder. Any specific behavior from package builders -should not be relied upon. - -Any other attributes should be specified as additional arguments to -`pkg_attributes`. -- cgit v1.2.3 From ab9549282690bdeffb4d13af670bcbc5a6008c23 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Wed, 21 Dec 2022 13:38:33 -0500 Subject: remove extra file --- doc_build/toc.md.tpl | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 doc_build/toc.md.tpl diff --git a/doc_build/toc.md.tpl b/doc_build/toc.md.tpl deleted file mode 100644 index 224f363..0000000 --- a/doc_build/toc.md.tpl +++ /dev/null @@ -1,28 +0,0 @@ -# rules_pkg - {VERSION} - - -- cgit v1.2.3 From 8ec289604beb093bdb1b9ebc8465c5ec45ee93c0 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Fri, 20 Jan 2023 01:03:11 -0500 Subject: bring back the policy checker example --- examples/policy_checker/BUILD | 63 +++++++++++++++ examples/policy_checker/license_policy.bzl | 56 +++++++++++++ examples/policy_checker/license_policy_check.bzl | 94 ++++++++++++++++++++++ .../policy_checker/license_policy_provider.bzl | 24 ++++++ examples/src/BUILD | 16 ++++ examples/vendor/acme/BUILD | 5 +- examples/vendor/libhhgttg/BUILD | 5 +- 7 files changed, 261 insertions(+), 2 deletions(-) create mode 100644 examples/policy_checker/BUILD create mode 100644 examples/policy_checker/license_policy.bzl create mode 100644 examples/policy_checker/license_policy_check.bzl create mode 100644 examples/policy_checker/license_policy_provider.bzl diff --git a/examples/policy_checker/BUILD b/examples/policy_checker/BUILD new file mode 100644 index 0000000..a18bcd2 --- /dev/null +++ b/examples/policy_checker/BUILD @@ -0,0 +1,63 @@ +# Example of automated license policy definitions. + +load("@rules_license//examples/policy_checker:license_policy.bzl", "license_policy") +load("@rules_license//examples/policy_checker:license_policy_check.bzl", "license_policy_check") + +package(default_package_metadata = ["//:license", "//:package_info"]) + +# license_policy rules generally appear in a central location per workspace. That +# should be access controlled by the policy team. + +# A production service can use licenses with most conditions +license_policy( + name = "production_service", + conditions = [ + "notice", + "restricted_if_statically_linked", + ], +) + +# A mobile application usually can not allow end-user replacable libraries. +# So LGPL code (which is restricted_if_statically_linked) can not be used. +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", + ], +) + +# Now we might build checks of critical applications against policies +# +# Questions to consider? +# - Your organization migth want to fold these kinds of checks into +# wrapper macros around the rules which generate services and apps +# - You might want to distribute checks to rules alongside the products +# - Or, you might want to consolidate them in a single place where your +# compliance team owns them, as this example does + +license_policy_check( + name = "check_server", + policy = ":production_service", + target = "//examples/src:my_server", +) + + +# This is marked manual, so bazel test ... does not fail. Try it yourself with +# bazel build :check_violating_server +license_policy_check( + name = "check_violating_server", + policy = ":production_service", + tags = [ + "manual", + ], + target = "//examples/src:my_violating_server", +) diff --git a/examples/policy_checker/license_policy.bzl b/examples/policy_checker/license_policy.bzl new file mode 100644 index 0000000..51d6776 --- /dev/null +++ b/examples/policy_checker/license_policy.bzl @@ -0,0 +1,56 @@ +# 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//examples/policy_checker: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/examples/policy_checker/license_policy_check.bzl b/examples/policy_checker/license_policy_check.bzl new file mode 100644 index 0000000..2692a01 --- /dev/null +++ b/examples/policy_checker/license_policy_check.bzl @@ -0,0 +1,94 @@ +# 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//examples/policy_checker:license_policy_provider.bzl", + "LicensePolicyInfo", +) +load( + "@rules_license//rules:gather_licenses_info.bzl", + "gather_licenses_info", +) +load( + "@rules_license//rules:providers.bzl", + "LicenseInfo", + "TransitiveLicensesInfo", +) + +# This is a crude example of the kind of thing which can be done. +def _license_policy_check_impl(ctx): + policy = ctx.attr.policy[LicensePolicyInfo] + allowed_conditions = policy.conditions + if TransitiveLicensesInfo in ctx.attr.target: + for license in ctx.attr.target[TransitiveLicensesInfo].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, + )) + + if LicenseInfo in ctx.attr.target: + for license in ctx.attr.target[LicenseInfo].licenses.to_list(): + print(license) + 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/examples/policy_checker/license_policy_provider.bzl b/examples/policy_checker/license_policy_provider.bzl new file mode 100644 index 0000000..caecce8 --- /dev/null +++ b/examples/policy_checker/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 = { + "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/examples/src/BUILD b/examples/src/BUILD index ecab5da..0bcb938 100644 --- a/examples/src/BUILD +++ b/examples/src/BUILD @@ -16,6 +16,11 @@ load("@rules_license//rules:compliance.bzl", "check_license", "licenses_used") load("@rules_license//examples/vendor/constant_gen:defs.bzl", "constant_gen") +package( + default_package_metadata = ["//:license", "//:package_info"], + default_visibility = ["//visibility:public"], +) + cc_binary( name = "my_server", srcs = ["server.cc"], @@ -59,3 +64,14 @@ py_test( "@rules_license//tests:license_test_utils", ], ) + +# This server uses something under a restricted license +cc_binary( + name = "my_violating_server", + srcs = ["server.cc"], + deps = [ + ":message", + "@rules_license//examples/vendor/acme", + "@rules_license//examples/vendor/libhhgttg", + ], +) diff --git a/examples/vendor/acme/BUILD b/examples/vendor/acme/BUILD index 814957d..2e98d98 100644 --- a/examples/vendor/acme/BUILD +++ b/examples/vendor/acme/BUILD @@ -15,7 +15,10 @@ load("@rules_license//rules:license.bzl", "license") -package(default_applicable_licenses = [":license"]) +package( + default_applicable_licenses = [":license"], + default_visibility = ["//visibility:public"], +) # The default license for an entire package is typically named "license". license( diff --git a/examples/vendor/libhhgttg/BUILD b/examples/vendor/libhhgttg/BUILD index 44d2d61..15c7655 100644 --- a/examples/vendor/libhhgttg/BUILD +++ b/examples/vendor/libhhgttg/BUILD @@ -18,7 +18,10 @@ 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"]) +package( + default_applicable_licenses = [":license"], + default_visibility = ["//visibility:public"], +) # The default license for an entire package is typically named "license". license( -- cgit v1.2.3 From d87630d1e51e91f2df3b4f95fdef6cd97f12e3de Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Tue, 7 Feb 2023 00:50:29 -0500 Subject: Replace golden file tests with ones based on the structure of the tests Change the test helper library to use path suffixes rather than complete paths so it is immune to relocating the code if you vendor it in. --- examples/src/server_report.golden | 3 -- examples/vendor/constant_gen/BUILD | 23 ++++++------ .../constant_gen/generated_code_licenses.golden | 41 --------------------- .../vendor/constant_gen/generator_licenses.golden | 34 ------------------ .../vendor/constant_gen/verify_licenses_test.py | 42 ++++++++++++++++++++++ tests/BUILD | 22 +++--------- tests/hello_cc_copyrights.golden | 3 -- tests/hello_java_copyrights.golden | 2 -- tests/hello_licenses_test.py | 13 +++++++ tests/license_test_utils.py | 34 ++++++++++++------ 10 files changed, 93 insertions(+), 124 deletions(-) delete mode 100644 examples/src/server_report.golden delete mode 100644 examples/vendor/constant_gen/generated_code_licenses.golden delete mode 100644 examples/vendor/constant_gen/generator_licenses.golden create mode 100644 examples/vendor/constant_gen/verify_licenses_test.py delete mode 100755 tests/hello_cc_copyrights.golden delete mode 100755 tests/hello_java_copyrights.golden diff --git a/examples/src/server_report.golden b/examples/src/server_report.golden deleted file mode 100644 index 8be07a0..0000000 --- a/examples/src/server_report.golden +++ /dev/null @@ -1,3 +0,0 @@ -= //examples/vendor/constant_gen:license_for_emitted_code - kind: @//examples/my_org/licenses:unencumbered - conditions: [] diff --git a/examples/vendor/constant_gen/BUILD b/examples/vendor/constant_gen/BUILD index e70f489..26e86b6 100644 --- a/examples/vendor/constant_gen/BUILD +++ b/examples/vendor/constant_gen/BUILD @@ -14,7 +14,6 @@ # 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//tools:test_helpers.bzl", "golden_test") load("@rules_license//rules:license.bzl", "license") load(":defs.bzl", "constant_gen") @@ -63,21 +62,21 @@ licenses_used( 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", +py_test( + name = "verify_licenses_test", + srcs = ["verify_licenses_test.py"], + data = [ + ":generator_licenses.json", + ":generated_code_licenses.json", + ], + python_version = "PY3", + deps = [ + "//tests:license_test_utils", + ], ) - diff --git a/examples/vendor/constant_gen/generated_code_licenses.golden b/examples/vendor/constant_gen/generated_code_licenses.golden deleted file mode 100644 index c4c8ef1..0000000 --- a/examples/vendor/constant_gen/generated_code_licenses.golden +++ /dev/null @@ -1,41 +0,0 @@ -[ - { - "top_level_target": "//examples/vendor/constant_gen:libhello", - "dependencies": [ - { - "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" - ] - } - ], - "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 deleted file mode 100644 index 4b2f175..0000000 --- a/examples/vendor/constant_gen/generator_licenses.golden +++ /dev/null @@ -1,34 +0,0 @@ -[ - { - "top_level_target": "//examples/vendor/constant_gen:constant_generator", - "dependencies": [ - { - "target_under_license": "//examples/vendor/constant_gen:constant_generator", - "licenses": [ - "//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/constant_gen/verify_licenses_test.py b/examples/vendor/constant_gen/verify_licenses_test.py new file mode 100644 index 0000000..a3eb847 --- /dev/null +++ b/examples/vendor/constant_gen/verify_licenses_test.py @@ -0,0 +1,42 @@ +"""Test that we see the expected licenses for some generated code.""" + +import codecs +import os + +import unittest +from tests import license_test_utils + +class VerifyLicensesTest(unittest.TestCase): + + def test_generater_license(self): + package_base = license_test_utils.LICENSE_PACKAGE_BASE + licenses_info = license_test_utils.load_licenses_info( + os.path.join(os.path.dirname(__file__), "generator_licenses.json")) + + expected = { + "examples/vendor/constant_gen:constant_generator": [ + "examples/vendor/constant_gen:license" + ], + } + license_test_utils.check_licenses_of_dependencies( + self, licenses_info, expected) + + def test_generated_code_license(self): + package_base = license_test_utils.LICENSE_PACKAGE_BASE + licenses_info = license_test_utils.load_licenses_info( + os.path.join(os.path.dirname(__file__), "generated_code_licenses.json")) + + expected = { + "examples/vendor/constant_gen:libhello": [ + "/constant_gen:license_for_emitted_code", + ], + "examples/vendor/constant_gen:libhello_src_": [ + "/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/tests/BUILD b/tests/BUILD index 10b8560..1d7961f 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -1,6 +1,5 @@ # Test cases for license rules. -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") @@ -119,7 +118,10 @@ licenses_used( py_test( name = "hello_licenses_test", srcs = ["hello_licenses_test.py"], - data = [":hello_licenses.json"], + data = [ + ":hello_licenses.json", + ":hello_cc_copyrights.txt", + ], python_version = "PY3", deps = [ ":license_test_utils", @@ -142,19 +144,3 @@ check_license( ":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", -) - -exports_files([ - "hello_licenses.golden", -]) diff --git a/tests/hello_cc_copyrights.golden b/tests/hello_cc_copyrights.golden deleted file mode 100755 index 38f67ad..0000000 --- a/tests/hello_cc_copyrights.golden +++ /dev/null @@ -1,3 +0,0 @@ -package(A test case package/0.0.4), copyright(Copyright © 2019 Uncle Toasty) -package(A test case package), copyright() - diff --git a/tests/hello_java_copyrights.golden b/tests/hello_java_copyrights.golden deleted file mode 100755 index 0ba7362..0000000 --- a/tests/hello_java_copyrights.golden +++ /dev/null @@ -1,2 +0,0 @@ -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 index 465688f..adfe92b 100644 --- a/tests/hello_licenses_test.py +++ b/tests/hello_licenses_test.py @@ -1,5 +1,6 @@ """Tests for google3.tools.build_defs.license.tests.hello_licenses.""" +import codecs import os import unittest @@ -29,6 +30,18 @@ class HelloLicensesTest(unittest.TestCase): license_test_utils.check_licenses_of_dependencies( self, licenses_info, expected) + def test_has_expected_copyrights(self): + copyrights_file = os.path.join(os.path.dirname(__file__), + "hello_cc_copyrights.txt") + with codecs.open(copyrights_file, encoding="utf-8") as inp: + copyrights = inp.read().split('\n') + self.assertIn( + "package(A test case package/0.0.4), copyright(Copyright © 2019 Uncle Toasty)", + copyrights) + self.assertIn( + "package(A test case package), copyright()", + copyrights) + if __name__ == "__main__": unittest.main() diff --git a/tests/license_test_utils.py b/tests/license_test_utils.py index 2c5a18a..cc29e59 100644 --- a/tests/license_test_utils.py +++ b/tests/license_test_utils.py @@ -3,7 +3,6 @@ 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 = "/" @@ -52,21 +51,34 @@ def check_licenses_of_dependencies(test_case, licenses_info, expected, 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. + expected: (dict) map of target name suffixes to the licenses they are under. 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"]) + 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. + target_names = ",".join(deps_to_licenses.keys()) + # This is n**2, but N is typically < 3 or we are doing this wrong. + for want_target, want_licenses in expected.items(): + found_target = False + for got_target, got_licenses in deps_to_licenses.items(): + if got_target.endswith(want_target): + found_target = True + test_case.assertEqual(len(want_licenses), len(got_licenses)) + found_license = False + for want_l in want_licenses: + for got_l in got_licenses: + if got_l.endswith(want_l): + found_license = True + break + test_case.assertTrue( + found_license, + msg="license (%s) not a suffix in %s" % (want_l, got_licenses)) + break + test_case.assertTrue( + found_target, + msg="target (%s) not a suffix in [%s]" % (want_target, target_names)) -- cgit v1.2.3 From 3a9a508a841dea22a0a33f0fea84a55a1c21d56f Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Tue, 7 Feb 2023 12:49:50 -0500 Subject: remove unused code --- examples/vendor/constant_gen/verify_licenses_test.py | 2 -- tests/hello_licenses_test.py | 5 ----- 2 files changed, 7 deletions(-) diff --git a/examples/vendor/constant_gen/verify_licenses_test.py b/examples/vendor/constant_gen/verify_licenses_test.py index a3eb847..4fddef8 100644 --- a/examples/vendor/constant_gen/verify_licenses_test.py +++ b/examples/vendor/constant_gen/verify_licenses_test.py @@ -9,7 +9,6 @@ from tests import license_test_utils class VerifyLicensesTest(unittest.TestCase): def test_generater_license(self): - package_base = license_test_utils.LICENSE_PACKAGE_BASE licenses_info = license_test_utils.load_licenses_info( os.path.join(os.path.dirname(__file__), "generator_licenses.json")) @@ -22,7 +21,6 @@ class VerifyLicensesTest(unittest.TestCase): self, licenses_info, expected) def test_generated_code_license(self): - package_base = license_test_utils.LICENSE_PACKAGE_BASE licenses_info = license_test_utils.load_licenses_info( os.path.join(os.path.dirname(__file__), "generated_code_licenses.json")) diff --git a/tests/hello_licenses_test.py b/tests/hello_licenses_test.py index adfe92b..de62c7f 100644 --- a/tests/hello_licenses_test.py +++ b/tests/hello_licenses_test.py @@ -10,13 +10,8 @@ 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": [ -- cgit v1.2.3 From 4c4d2e9c71c5e05faa128943e35f511688217451 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Wed, 8 Feb 2023 16:01:29 -0500 Subject: Grab back of little style nits which will make it easier to sync this code back and forth with the Google branches. - restore copyright notices (This keeps compliance teams happy) - change public visibilty to restricted ones, so end users can not depend on pieces of the vendored in library - remove printf debugging. --- examples/my_org/licenses/BUILD | 2 +- examples/policy_checker/BUILD | 6 +++--- examples/policy_checker/license_policy_check.bzl | 1 - examples/src/BUILD | 2 +- examples/vendor/acme/BUILD | 2 +- examples/vendor/constant_gen/BUILD | 2 +- examples/vendor/constant_gen/defs.bzl | 13 +++++++++++++ examples/vendor/libhhgttg/BUILD | 2 +- tests/BUILD | 8 ++++---- tests/apps/an_app.cc | 13 +++++++++++++ tests/apps/an_app_licenses_test.py | 14 +++++++++++++- tests/legacy/BUILD | 17 +++++++++++++++++ tests/legacy/file_under_notice.cc | 14 ++++++++++++++ tests/thrdparty/new_style_lib.cc | 13 +++++++++++++ tools/checker_demo.py | 2 -- 15 files changed, 95 insertions(+), 16 deletions(-) create mode 100644 tests/legacy/BUILD create mode 100644 tests/legacy/file_under_notice.cc diff --git a/examples/my_org/licenses/BUILD b/examples/my_org/licenses/BUILD index f17bfa3..c200d37 100644 --- a/examples/my_org/licenses/BUILD +++ b/examples/my_org/licenses/BUILD @@ -66,6 +66,6 @@ license_kind( license_kind( name = "acme_corp_paid", conditions = [ - "whitelist:acme_corp_paid", + "allowlist:acme_corp_paid", ], ) diff --git a/examples/policy_checker/BUILD b/examples/policy_checker/BUILD index a18bcd2..49f77aa 100644 --- a/examples/policy_checker/BUILD +++ b/examples/policy_checker/BUILD @@ -27,11 +27,11 @@ license_policy( ) license_policy( - name = "special_whitelisted_app", - # There could be a whitelist of targets here. + name = "special_allowlisted_app", + # There could be a allowlist of targets here. conditions = [ "notice", - "whitelist:acme_corp_paid", + "allowlist:acme_corp_paid", ], ) diff --git a/examples/policy_checker/license_policy_check.bzl b/examples/policy_checker/license_policy_check.bzl index 2692a01..32db845 100644 --- a/examples/policy_checker/license_policy_check.bzl +++ b/examples/policy_checker/license_policy_check.bzl @@ -45,7 +45,6 @@ def _license_policy_check_impl(ctx): if LicenseInfo in ctx.attr.target: for license in ctx.attr.target[LicenseInfo].licenses.to_list(): - print(license) for kind in license.license_kinds: # print(kind.conditions) for condition in kind.conditions: diff --git a/examples/src/BUILD b/examples/src/BUILD index 0bcb938..8567e81 100644 --- a/examples/src/BUILD +++ b/examples/src/BUILD @@ -18,7 +18,7 @@ load("@rules_license//examples/vendor/constant_gen:defs.bzl", "constant_gen") package( default_package_metadata = ["//:license", "//:package_info"], - default_visibility = ["//visibility:public"], + default_visibility = ["//examples:__subpackages__"], ) cc_binary( diff --git a/examples/vendor/acme/BUILD b/examples/vendor/acme/BUILD index 2e98d98..09da19d 100644 --- a/examples/vendor/acme/BUILD +++ b/examples/vendor/acme/BUILD @@ -17,7 +17,7 @@ load("@rules_license//rules:license.bzl", "license") package( default_applicable_licenses = [":license"], - default_visibility = ["//visibility:public"], + default_visibility = ["//examples:__subpackages__"], ) # The default license for an entire package is typically named "license". diff --git a/examples/vendor/constant_gen/BUILD b/examples/vendor/constant_gen/BUILD index 26e86b6..5f2ff43 100644 --- a/examples/vendor/constant_gen/BUILD +++ b/examples/vendor/constant_gen/BUILD @@ -19,7 +19,7 @@ load(":defs.bzl", "constant_gen") package( default_applicable_licenses = [":license"], - default_visibility = ["//visibility:public"], + default_visibility = ["//examples:__subpackages__"], ) # The default license for an entire package is typically named "license". diff --git a/examples/vendor/constant_gen/defs.bzl b/examples/vendor/constant_gen/defs.bzl index f3eb715..33aa419 100644 --- a/examples/vendor/constant_gen/defs.bzl +++ b/examples/vendor/constant_gen/defs.bzl @@ -1,3 +1,16 @@ +# 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. """A trivial rule to turn a string into a C++ constant.""" def _constant_gen_impl(ctx): diff --git a/examples/vendor/libhhgttg/BUILD b/examples/vendor/libhhgttg/BUILD index 15c7655..b9e3991 100644 --- a/examples/vendor/libhhgttg/BUILD +++ b/examples/vendor/libhhgttg/BUILD @@ -20,7 +20,7 @@ load("@rules_license//rules:license.bzl", "license") # license. package( default_applicable_licenses = [":license"], - default_visibility = ["//visibility:public"], + default_visibility = ["//examples:__subpackages__"], ) # The default license for an entire package is typically named "license". diff --git a/tests/BUILD b/tests/BUILD index 1d7961f..a031df3 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -74,10 +74,10 @@ cc_library( ":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", - #], + deps = [ + "@rules_license//tests/legacy:another_library_with_legacy_license_clause", + "@rules_license//tests/legacy:library_with_legacy_license_clause", + ], ) java_binary( diff --git a/tests/apps/an_app.cc b/tests/apps/an_app.cc index 410f986..60f6fc5 100644 --- a/tests/apps/an_app.cc +++ b/tests/apps/an_app.cc @@ -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. #include #include diff --git a/tests/apps/an_app_licenses_test.py b/tests/apps/an_app_licenses_test.py index 9899e6c..0dbf8fb 100644 --- a/tests/apps/an_app_licenses_test.py +++ b/tests/apps/an_app_licenses_test.py @@ -1,4 +1,16 @@ -"""Tests for google3.tools.build_defs.license.tests.apps.an_app_licenses.""" +# 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. import os diff --git a/tests/legacy/BUILD b/tests/legacy/BUILD new file mode 100644 index 0000000..e065e22 --- /dev/null +++ b/tests/legacy/BUILD @@ -0,0 +1,17 @@ +# Example of an unmigrated package. + +package(default_visibility = [ + "//tests:__subpackages__", +]) + +licenses(["unencumbered"]) + +cc_library( + name = "library_with_legacy_license_clause", + srcs = ["file_under_notice.cc"], +) + +cc_library( + name = "another_library_with_legacy_license_clause", + srcs = ["file_under_notice.cc"], +) diff --git a/tests/legacy/file_under_notice.cc b/tests/legacy/file_under_notice.cc new file mode 100644 index 0000000..2fa8bfc --- /dev/null +++ b/tests/legacy/file_under_notice.cc @@ -0,0 +1,14 @@ +// 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. +int file_under_notice = 1; diff --git a/tests/thrdparty/new_style_lib.cc b/tests/thrdparty/new_style_lib.cc index 545e5b4..0c739b9 100644 --- a/tests/thrdparty/new_style_lib.cc +++ b/tests/thrdparty/new_style_lib.cc @@ -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. #include #include diff --git a/tools/checker_demo.py b/tools/checker_demo.py index 1075621..6cdf07f 100644 --- a/tools/checker_demo.py +++ b/tools/checker_demo.py @@ -90,7 +90,6 @@ def _do_copyright_notices(out, licenses): 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'])) @@ -122,7 +121,6 @@ def main(): 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: -- cgit v1.2.3 From 55a91aa23263dac35a95b2a7b98797cecb733cf5 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Wed, 8 Feb 2023 16:17:39 -0500 Subject: javacopts --- tests/BUILD | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/BUILD b/tests/BUILD index a031df3..ab2438f 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -87,6 +87,7 @@ java_binary( applicable_licenses = [ ":license_for_extra_feature", ], + javacopts = ["-Xep:DefaultPackage:OFF"], main_class = "Hello", deps = [ ":j_bar", @@ -96,6 +97,7 @@ java_binary( java_library( name = "j_bar", srcs = ["Bar.java"], + javacopts = ["-Xep:DefaultPackage:OFF"], ) check_license( -- cgit v1.2.3 From 51ce23181db90d4ce2daeb77fd891ea4d6f5633c Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Wed, 8 Feb 2023 17:07:31 -0500 Subject: add bzlmod files to the distro --- BUILD | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BUILD b/BUILD index 40cfcad..31520c4 100644 --- a/BUILD +++ b/BUILD @@ -55,8 +55,10 @@ filegroup( "*.bzl", "*.md", ]) + [ + "MODULE.bazel", "BUILD", "LICENSE", + "WORKSPACE.bzlmod", ], visibility = ["//distro:__pkg__"], ) -- cgit v1.2.3 From df22d7959456a7f3aa8298f41080ccd4126b7985 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Thu, 9 Feb 2023 14:11:13 -0500 Subject: rename vendor to vndor so that compliance lint stops complaining --- examples/src/BUILD | 6 +- examples/src/server_licenses_test.py | 4 +- examples/vendor/README.md | 6 -- examples/vendor/acme/ACME_LICENSE | 3 - examples/vendor/acme/BUILD | 35 --------- examples/vendor/acme/coyote.cc | 16 ----- examples/vendor/constant_gen/BUILD | 82 ---------------------- examples/vendor/constant_gen/LICENSE | 1 - examples/vendor/constant_gen/LICENSE.on_output | 1 - examples/vendor/constant_gen/constant_generator.py | 31 -------- examples/vendor/constant_gen/defs.bzl | 58 --------------- .../vendor/constant_gen/verify_licenses_test.py | 40 ----------- examples/vendor/libhhgttg/BUILD | 38 ---------- examples/vendor/libhhgttg/LICENSE | 2 - examples/vendor/libhhgttg/answer.cc | 15 ---- examples/vndor/README.md | 6 ++ examples/vndor/acme/ACME_LICENSE | 3 + examples/vndor/acme/BUILD | 35 +++++++++ examples/vndor/acme/coyote.cc | 16 +++++ examples/vndor/constant_gen/BUILD | 82 ++++++++++++++++++++++ examples/vndor/constant_gen/LICENSE | 1 + examples/vndor/constant_gen/LICENSE.on_output | 1 + examples/vndor/constant_gen/constant_generator.py | 31 ++++++++ examples/vndor/constant_gen/defs.bzl | 58 +++++++++++++++ .../vndor/constant_gen/verify_licenses_test.py | 40 +++++++++++ examples/vndor/libhhgttg/BUILD | 38 ++++++++++ examples/vndor/libhhgttg/LICENSE | 2 + examples/vndor/libhhgttg/answer.cc | 15 ++++ 28 files changed, 333 insertions(+), 333 deletions(-) delete mode 100644 examples/vendor/README.md delete mode 100644 examples/vendor/acme/ACME_LICENSE delete mode 100644 examples/vendor/acme/BUILD delete mode 100644 examples/vendor/acme/coyote.cc delete mode 100644 examples/vendor/constant_gen/BUILD delete mode 100644 examples/vendor/constant_gen/LICENSE delete mode 100644 examples/vendor/constant_gen/LICENSE.on_output delete mode 100644 examples/vendor/constant_gen/constant_generator.py delete mode 100644 examples/vendor/constant_gen/defs.bzl delete mode 100644 examples/vendor/constant_gen/verify_licenses_test.py delete mode 100644 examples/vendor/libhhgttg/BUILD delete mode 100644 examples/vendor/libhhgttg/LICENSE delete mode 100644 examples/vendor/libhhgttg/answer.cc create mode 100644 examples/vndor/README.md create mode 100644 examples/vndor/acme/ACME_LICENSE create mode 100644 examples/vndor/acme/BUILD create mode 100644 examples/vndor/acme/coyote.cc create mode 100644 examples/vndor/constant_gen/BUILD create mode 100644 examples/vndor/constant_gen/LICENSE create mode 100644 examples/vndor/constant_gen/LICENSE.on_output create mode 100644 examples/vndor/constant_gen/constant_generator.py create mode 100644 examples/vndor/constant_gen/defs.bzl create mode 100644 examples/vndor/constant_gen/verify_licenses_test.py create mode 100644 examples/vndor/libhhgttg/BUILD create mode 100644 examples/vndor/libhhgttg/LICENSE create mode 100644 examples/vndor/libhhgttg/answer.cc diff --git a/examples/src/BUILD b/examples/src/BUILD index 8567e81..cd5e985 100644 --- a/examples/src/BUILD +++ b/examples/src/BUILD @@ -14,7 +14,7 @@ # 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//examples/vndor/constant_gen:defs.bzl", "constant_gen") package( default_package_metadata = ["//:license", "//:package_info"], @@ -71,7 +71,7 @@ cc_binary( srcs = ["server.cc"], deps = [ ":message", - "@rules_license//examples/vendor/acme", - "@rules_license//examples/vendor/libhhgttg", + "@rules_license//examples/vndor/acme", + "@rules_license//examples/vndor/libhhgttg", ], ) diff --git a/examples/src/server_licenses_test.py b/examples/src/server_licenses_test.py index c7c30da..66a20a5 100644 --- a/examples/src/server_licenses_test.py +++ b/examples/src/server_licenses_test.py @@ -32,10 +32,10 @@ class ServerLicensesTest(unittest.TestCase): expected = { "/examples/src:message_src_": [ - "/examples/vendor/constant_gen:license_for_emitted_code" + "/examples/vndor/constant_gen:license_for_emitted_code" ], "/examples/src:message": [ - "/examples/vendor/constant_gen:license_for_emitted_code" + "/examples/vndor/constant_gen:license_for_emitted_code" ], } license_test_utils.check_licenses_of_dependencies( diff --git a/examples/vendor/README.md b/examples/vendor/README.md deleted file mode 100644 index 273dea6..0000000 --- a/examples/vendor/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# 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 deleted file mode 100644 index 53a5daf..0000000 --- a/examples/vendor/acme/ACME_LICENSE +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 09da19d..0000000 --- a/examples/vendor/acme/BUILD +++ /dev/null @@ -1,35 +0,0 @@ -# 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 = ["//examples:__subpackages__"], -) - -# 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 deleted file mode 100644 index d637855..0000000 --- a/examples/vendor/acme/coyote.cc +++ /dev/null @@ -1,16 +0,0 @@ -// 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. -bool caught_road_runner() { - return false; -} diff --git a/examples/vendor/constant_gen/BUILD b/examples/vendor/constant_gen/BUILD deleted file mode 100644 index 5f2ff43..0000000 --- a/examples/vendor/constant_gen/BUILD +++ /dev/null @@ -1,82 +0,0 @@ -# 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(":defs.bzl", "constant_gen") - -package( - default_applicable_licenses = [":license"], - default_visibility = ["//examples:__subpackages__"], -) - -# 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_kinds = [ - "@rules_license//examples/my_org/licenses:unencumbered", - ], - license_text = "LICENSE.on_output", -) - -# 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"], -) - -licenses_used( - name = "generated_code_licenses", - # Note: using default output file name - deps = [":libhello"], -) - -py_test( - name = "verify_licenses_test", - srcs = ["verify_licenses_test.py"], - data = [ - ":generator_licenses.json", - ":generated_code_licenses.json", - ], - python_version = "PY3", - deps = [ - "//tests:license_test_utils", - ], -) diff --git a/examples/vendor/constant_gen/LICENSE b/examples/vendor/constant_gen/LICENSE deleted file mode 100644 index 861da0d..0000000 --- a/examples/vendor/constant_gen/LICENSE +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index b699638..0000000 --- a/examples/vendor/constant_gen/LICENSE.on_output +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 432b6be..0000000 --- a/examples/vendor/constant_gen/constant_generator.py +++ /dev/null @@ -1,31 +0,0 @@ -# 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 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 deleted file mode 100644 index 33aa419..0000000 --- a/examples/vendor/constant_gen/defs.bzl +++ /dev/null @@ -1,58 +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. -"""A trivial rule to turn a string into a C++ constant.""" - -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 = "exec", - ), - }, -) - -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/verify_licenses_test.py b/examples/vendor/constant_gen/verify_licenses_test.py deleted file mode 100644 index 4fddef8..0000000 --- a/examples/vendor/constant_gen/verify_licenses_test.py +++ /dev/null @@ -1,40 +0,0 @@ -"""Test that we see the expected licenses for some generated code.""" - -import codecs -import os - -import unittest -from tests import license_test_utils - -class VerifyLicensesTest(unittest.TestCase): - - def test_generater_license(self): - licenses_info = license_test_utils.load_licenses_info( - os.path.join(os.path.dirname(__file__), "generator_licenses.json")) - - expected = { - "examples/vendor/constant_gen:constant_generator": [ - "examples/vendor/constant_gen:license" - ], - } - license_test_utils.check_licenses_of_dependencies( - self, licenses_info, expected) - - def test_generated_code_license(self): - licenses_info = license_test_utils.load_licenses_info( - os.path.join(os.path.dirname(__file__), "generated_code_licenses.json")) - - expected = { - "examples/vendor/constant_gen:libhello": [ - "/constant_gen:license_for_emitted_code", - ], - "examples/vendor/constant_gen:libhello_src_": [ - "/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/vendor/libhhgttg/BUILD b/examples/vendor/libhhgttg/BUILD deleted file mode 100644 index b9e3991..0000000 --- a/examples/vendor/libhhgttg/BUILD +++ /dev/null @@ -1,38 +0,0 @@ -# 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. - -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 = ["//examples:__subpackages__"], -) - -# 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 deleted file mode 100644 index 660e329..0000000 --- a/examples/vendor/libhhgttg/LICENSE +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index 440bc62..0000000 --- a/examples/vendor/libhhgttg/answer.cc +++ /dev/null @@ -1,15 +0,0 @@ -// 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. - -int answer = 42; diff --git a/examples/vndor/README.md b/examples/vndor/README.md new file mode 100644 index 0000000..273dea6 --- /dev/null +++ b/examples/vndor/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/vndor/acme/ACME_LICENSE b/examples/vndor/acme/ACME_LICENSE new file mode 100644 index 0000000..53a5daf --- /dev/null +++ b/examples/vndor/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/vndor/acme/BUILD b/examples/vndor/acme/BUILD new file mode 100644 index 0000000..09da19d --- /dev/null +++ b/examples/vndor/acme/BUILD @@ -0,0 +1,35 @@ +# 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 = ["//examples:__subpackages__"], +) + +# 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/vndor/acme/coyote.cc b/examples/vndor/acme/coyote.cc new file mode 100644 index 0000000..d637855 --- /dev/null +++ b/examples/vndor/acme/coyote.cc @@ -0,0 +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. +bool caught_road_runner() { + return false; +} diff --git a/examples/vndor/constant_gen/BUILD b/examples/vndor/constant_gen/BUILD new file mode 100644 index 0000000..5f2ff43 --- /dev/null +++ b/examples/vndor/constant_gen/BUILD @@ -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. +# 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(":defs.bzl", "constant_gen") + +package( + default_applicable_licenses = [":license"], + default_visibility = ["//examples:__subpackages__"], +) + +# 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_kinds = [ + "@rules_license//examples/my_org/licenses:unencumbered", + ], + license_text = "LICENSE.on_output", +) + +# 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"], +) + +licenses_used( + name = "generated_code_licenses", + # Note: using default output file name + deps = [":libhello"], +) + +py_test( + name = "verify_licenses_test", + srcs = ["verify_licenses_test.py"], + data = [ + ":generator_licenses.json", + ":generated_code_licenses.json", + ], + python_version = "PY3", + deps = [ + "//tests:license_test_utils", + ], +) diff --git a/examples/vndor/constant_gen/LICENSE b/examples/vndor/constant_gen/LICENSE new file mode 100644 index 0000000..861da0d --- /dev/null +++ b/examples/vndor/constant_gen/LICENSE @@ -0,0 +1 @@ +This code is provided under a license which contains some restrictions. diff --git a/examples/vndor/constant_gen/LICENSE.on_output b/examples/vndor/constant_gen/LICENSE.on_output new file mode 100644 index 0000000..b699638 --- /dev/null +++ b/examples/vndor/constant_gen/LICENSE.on_output @@ -0,0 +1 @@ +The generated output from constant_gen has no license encumberances. diff --git a/examples/vndor/constant_gen/constant_generator.py b/examples/vndor/constant_gen/constant_generator.py new file mode 100644 index 0000000..432b6be --- /dev/null +++ b/examples/vndor/constant_gen/constant_generator.py @@ -0,0 +1,31 @@ +# 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 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/vndor/constant_gen/defs.bzl b/examples/vndor/constant_gen/defs.bzl new file mode 100644 index 0000000..518437c --- /dev/null +++ b/examples/vndor/constant_gen/defs.bzl @@ -0,0 +1,58 @@ +# 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. +"""A trivial rule to turn a string into a C++ constant.""" + +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/vndor/constant_gen:constant_generator"), + executable = True, + allow_files = True, + cfg = "exec", + ), + }, +) + +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/vndor/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/vndor/constant_gen:license_for_emitted_code"], + ) diff --git a/examples/vndor/constant_gen/verify_licenses_test.py b/examples/vndor/constant_gen/verify_licenses_test.py new file mode 100644 index 0000000..917e600 --- /dev/null +++ b/examples/vndor/constant_gen/verify_licenses_test.py @@ -0,0 +1,40 @@ +"""Test that we see the expected licenses for some generated code.""" + +import codecs +import os + +import unittest +from tests import license_test_utils + +class VerifyLicensesTest(unittest.TestCase): + + def test_generater_license(self): + licenses_info = license_test_utils.load_licenses_info( + os.path.join(os.path.dirname(__file__), "generator_licenses.json")) + + expected = { + "examples/vndor/constant_gen:constant_generator": [ + "examples/vndor/constant_gen:license" + ], + } + license_test_utils.check_licenses_of_dependencies( + self, licenses_info, expected) + + def test_generated_code_license(self): + licenses_info = license_test_utils.load_licenses_info( + os.path.join(os.path.dirname(__file__), "generated_code_licenses.json")) + + expected = { + "examples/vndor/constant_gen:libhello": [ + "/constant_gen:license_for_emitted_code", + ], + "examples/vndor/constant_gen:libhello_src_": [ + "/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/vndor/libhhgttg/BUILD b/examples/vndor/libhhgttg/BUILD new file mode 100644 index 0000000..b9e3991 --- /dev/null +++ b/examples/vndor/libhhgttg/BUILD @@ -0,0 +1,38 @@ +# 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. + +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 = ["//examples:__subpackages__"], +) + +# 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/vndor/libhhgttg/LICENSE b/examples/vndor/libhhgttg/LICENSE new file mode 100644 index 0000000..660e329 --- /dev/null +++ b/examples/vndor/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/vndor/libhhgttg/answer.cc b/examples/vndor/libhhgttg/answer.cc new file mode 100644 index 0000000..440bc62 --- /dev/null +++ b/examples/vndor/libhhgttg/answer.cc @@ -0,0 +1,15 @@ +// 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. + +int answer = 42; -- cgit v1.2.3 From 7fab43b4a46ec75fff051d5cd2726898273aad83 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Tue, 14 Feb 2023 00:15:25 -0500 Subject: fix buildifier warnings --- rules/gather_metadata.bzl | 6 ++---- rules/sbom.bzl | 25 +------------------------ 2 files changed, 3 insertions(+), 28 deletions(-) diff --git a/rules/gather_metadata.bzl b/rules/gather_metadata.bzl index 9e96cba..4b86403 100644 --- a/rules/gather_metadata.bzl +++ b/rules/gather_metadata.bzl @@ -43,8 +43,8 @@ def _strip_null_repo(label): return s def _bazel_package(label): - l = _strip_null_repo(label) - return l[0:-(len(label.name) + 1)] + clean_label = _strip_null_repo(label) + return clean_label[0:-(len(label.name) + 1)] def _gather_metadata_info_impl(target, ctx): return gather_metadata_info_common(target, ctx, TransitiveMetadataInfo, NAMESPACES, [MetadataInfo, PackageInfo], should_traverse) @@ -251,8 +251,6 @@ def metadata_info_to_json(metadata_info): all_deps = [] for dep in sorted(metadata_info.deps.to_list(), key = lambda x: x.target_under_license): - metadata_used = [] - # Undo the concatenation applied when stored in the provider. dep_licenses = dep.licenses.split(",") all_deps.append(dep_template.format( diff --git a/rules/sbom.bzl b/rules/sbom.bzl index fb17adc..a4af726 100644 --- a/rules/sbom.bzl +++ b/rules/sbom.bzl @@ -32,10 +32,6 @@ def _generate_sbom_impl(ctx): licenses_file = ctx.actions.declare_file("_%s_licenses_info.json" % ctx.label.name) write_metadata_info(ctx, ctx.attr.deps, licenses_file) - license_files = [] - # if ctx.outputs.license_texts: - # license_files = get_licenses_mapping(ctx.attr.deps).keys() - # Now turn the big blob of data into something consumable. inputs = [licenses_file] outputs = [ctx.outputs.out] @@ -106,26 +102,6 @@ def manifest(name, deps, out = None, **kwargs): _manifest(name = name, deps = deps, out = out, **kwargs) -def _licenses_used_impl(ctx): - # Gather all licenses and make it available as JSON - write_metadata_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_metadata_info_and_write], - ), - "out": attr.output( - doc = """Output file.""", - mandatory = True, - ), - }, -) - def get_licenses_mapping(deps, warn = False): """Creates list of entries representing all licenses for the deps. @@ -154,6 +130,7 @@ def get_licenses_mapping(deps, warn = False): if type(lic.license_text) == "File": mappings[lic.license_text] = lic.package_name elif warn: + # buildifier: disable=print print("Legacy license %s not included, rule needs updating" % lic.license_text) return mappings -- cgit v1.2.3 From 88789cce44d2123f2814cea3c52c87b4d047d05b Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Tue, 14 Feb 2023 00:48:19 -0500 Subject: Split providers.bzl in two. - put all the core ones that are required to declare liceneses in providers.bzl - move all the ones needed to gather licenses into private/gather_liceneses.bzl This keeps ownership and visibility clearer. Users can depend on the values in provider.bzl. The structs in gathering_providers.bzl should be considered private to the implementation. Also, make it clear that MetadataInfo is experimental by renaming it. --- examples/policy_checker/license_policy_check.bzl | 7 +-- rules/compliance.bzl | 2 +- rules/gather_licenses_info.bzl | 2 +- rules/gather_metadata.bzl | 15 +++++-- rules/licenses_core.bzl | 3 ++ rules/package_info.bzl | 4 +- rules/private/gathering_providers.bzl | 54 ++++++++++++++++++++++++ rules/providers.bzl | 46 +++----------------- rules/sbom.bzl | 2 +- 9 files changed, 83 insertions(+), 52 deletions(-) create mode 100644 rules/private/gathering_providers.bzl diff --git a/examples/policy_checker/license_policy_check.bzl b/examples/policy_checker/license_policy_check.bzl index 32db845..b91c96f 100644 --- a/examples/policy_checker/license_policy_check.bzl +++ b/examples/policy_checker/license_policy_check.bzl @@ -22,11 +22,8 @@ load( "@rules_license//rules:gather_licenses_info.bzl", "gather_licenses_info", ) -load( - "@rules_license//rules:providers.bzl", - "LicenseInfo", - "TransitiveLicensesInfo", -) +load("@rules_license//rules:providers.bzl", "LicenseInfo") +load("@rules_license//rules:private/gathering_providers.bzl", "TransitiveLicensesInfo") # This is a crude example of the kind of thing which can be done. def _license_policy_check_impl(ctx): diff --git a/rules/compliance.bzl b/rules/compliance.bzl index 1792454..2127869 100644 --- a/rules/compliance.bzl +++ b/rules/compliance.bzl @@ -20,7 +20,7 @@ load( "write_licenses_info", ) load( - "@rules_license//rules:providers.bzl", + "@rules_license//rules:private/gathering_providers.bzl", "TransitiveLicensesInfo", ) diff --git a/rules/gather_licenses_info.bzl b/rules/gather_licenses_info.bzl index b676972..894d20d 100644 --- a/rules/gather_licenses_info.bzl +++ b/rules/gather_licenses_info.bzl @@ -20,7 +20,7 @@ load( "should_traverse", ) load( - "@rules_license//rules:providers.bzl", + "@rules_license//rules:private/gathering_providers.bzl", "TransitiveLicensesInfo", ) diff --git a/rules/gather_metadata.bzl b/rules/gather_metadata.bzl index 9e96cba..7f3124d 100644 --- a/rules/gather_metadata.bzl +++ b/rules/gather_metadata.bzl @@ -21,8 +21,11 @@ load( ) load( "@rules_license//rules:providers.bzl", - "MetadataInfo", + "ExperimentalMetadataInfo", "PackageInfo", +) +load( + "@rules_license//rules:private/gathering_providers.bzl", "TransitiveMetadataInfo", ) @@ -47,7 +50,13 @@ def _bazel_package(label): return l[0:-(len(label.name) + 1)] def _gather_metadata_info_impl(target, ctx): - return gather_metadata_info_common(target, ctx, TransitiveMetadataInfo, NAMESPACES, [MetadataInfo, PackageInfo], should_traverse) + return gather_metadata_info_common( + target, + ctx, + TransitiveMetadataInfo, + NAMESPACES, + [ExperimentalMetadataInfo, PackageInfo], + should_traverse) gather_metadata_info = aspect( doc = """Collects LicenseInfo providers into a single TransitiveMetadataInfo provider.""", @@ -283,7 +292,7 @@ def metadata_info_to_json(metadata_info): package_url = mi.package_url, package_version = mi.package_version, )) - # experimental: Support the MetadataInfo bag of data + # experimental: Support the ExperimentalMetadataInfo bag of data if mi.type == "package_info_alt": all_packages.append(package_info_template.format( label = _strip_null_repo(mi.label), diff --git a/rules/licenses_core.bzl b/rules/licenses_core.bzl index cf476a4..46b835c 100644 --- a/rules/licenses_core.bzl +++ b/rules/licenses_core.bzl @@ -18,6 +18,9 @@ load("@rules_license//rules:user_filtered_rule_kinds.bzl", "user_aspect_filters" load( "@rules_license//rules:providers.bzl", "LicenseInfo", +) +load( + "@rules_license//rules:private/gathering_providers.bzl", "LicensedTargetInfo", "TransitiveLicensesInfo", ) diff --git a/rules/package_info.bzl b/rules/package_info.bzl index c0bef0b..c79545f 100644 --- a/rules/package_info.bzl +++ b/rules/package_info.bzl @@ -15,7 +15,7 @@ load( "@rules_license//rules:providers.bzl", - "MetadataInfo", + "ExperimentalMetadataInfo", "PackageInfo", ) @@ -36,7 +36,7 @@ def _package_info_impl(ctx): package_version = ctx.attr.package_version, ) # Experimental alternate design, using a generic 'data' back to hold things - generic_provider = MetadataInfo( + generic_provider = ExperimentalMetadataInfo( type = "package_info_alt", label = ctx.label, data = { diff --git a/rules/private/gathering_providers.bzl b/rules/private/gathering_providers.bzl new file mode 100644 index 0000000..1c3740f --- /dev/null +++ b/rules/private/gathering_providers.bzl @@ -0,0 +1,54 @@ +# 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. +"""Providers for transitively gathering all license and package_info targets. + +Warning: This is private to the aspect that walks the tree. The API is subject +to change at any release. +""" + +LicensedTargetInfo = provider( + doc = """Lists the licenses directly used by a single target.""", + fields = { + "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() + +TransitiveMetadataInfo = provider( + doc = """The transitive set of licenses used by a target.""", + fields = { + "top_level_target": "Label: The top level target label we are examining.", + "other_metadata": "depset(ExperimentalMetatdataInfo)", + "licenses": "depset(LicenseInfo)", + "package_info": "depset(PackageInfo)", + + "target_under_license": "Label: A target which will be associated with some licenses.", + "deps": "depset(LicensedTargetInfo): The transitive list of dependencies that have licenses.", + "traces": "list(string) - diagnostic for tracing a dependency relationship to a target.", + }, +) diff --git a/rules/providers.bzl b/rules/providers.bzl index 3b1f090..33a7fb5 100644 --- a/rules/providers.bzl +++ b/rules/providers.bzl @@ -11,7 +11,12 @@ # 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.""" +"""Basic providers for license rules. + +This file should only contain the basic providers needed to create +license and package_info declarations. Providers needed to gather +them are declared in other places. +""" LicenseKindInfo = provider( doc = """Provides information about a license_kind instance.""", @@ -38,29 +43,6 @@ LicenseInfo = provider( }, ) -LicensedTargetInfo = provider( - doc = """Lists the licenses directly used by a single target.""", - fields = { - "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() - -# This is one way to do specify data PackageInfo = provider( doc = """Provides information about a package.""", fields = { @@ -75,7 +57,7 @@ PackageInfo = provider( # This is more extensible. Because of the provider implementation, having a big # dict of values rather than named fields is not much more costly. # Design choice. Replace data with actual providers, such as PackageInfo -MetadataInfo = provider( +ExperimentalMetadataInfo = provider( doc = """Generic bag of metadata.""", fields = { "type": "string: How to interpret data", @@ -83,17 +65,3 @@ MetadataInfo = provider( "data": "String->any: Map of names to values", } ) - -TransitiveMetadataInfo = provider( - doc = """The transitive set of licenses used by a target.""", - fields = { - "top_level_target": "Label: The top level target label we are examining.", - "other_metadata": "depset(MetatdataInfo)", - "licenses": "depset(LicenseInfo)", - "package_info": "depset(PackageInfo)", - - "target_under_license": "Label: A target which will be associated with some licenses.", - "deps": "depset(LicensedTargetInfo): The transitive list of dependencies that have licenses.", - "traces": "list(string) - diagnostic for tracing a dependency relationship to a target.", - }, -) diff --git a/rules/sbom.bzl b/rules/sbom.bzl index fb17adc..aaa4fa4 100644 --- a/rules/sbom.bzl +++ b/rules/sbom.bzl @@ -20,7 +20,7 @@ load( "write_metadata_info", ) load( - "@rules_license//rules:providers.bzl", + "@rules_license//rules:private/gathering_providers.bzl", "TransitiveLicensesInfo", ) -- cgit v1.2.3 From 7de72c96421484ad4d7c068fa58a68d57ea33fa1 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Mon, 27 Feb 2023 15:33:12 -0500 Subject: Make rules/private a distinct package. That allows for better visibility control. --- distro/BUILD | 1 + doc_build/BUILD | 1 + examples/policy_checker/license_policy_check.bzl | 2 +- rules/compliance.bzl | 2 +- rules/gather_licenses_info.bzl | 2 +- rules/gather_metadata.bzl | 2 +- rules/licenses_core.bzl | 2 +- rules/private/BUILD | 35 ++++++++++++++++++++++++ rules/sbom.bzl | 2 +- 9 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 rules/private/BUILD diff --git a/distro/BUILD b/distro/BUILD index a79d54b..693d1ac 100644 --- a/distro/BUILD +++ b/distro/BUILD @@ -37,6 +37,7 @@ pkg_tar( "//licenses/generic:standard_package", "//licenses/spdx:standard_package", "//rules:standard_package", + "//rules/private:standard_package", "//tools:standard_package", ], extension = "tar.gz", diff --git a/doc_build/BUILD b/doc_build/BUILD index 5aadfde..87386f1 100644 --- a/doc_build/BUILD +++ b/doc_build/BUILD @@ -89,6 +89,7 @@ bzl_library( srcs = [ "//:version.bzl", "//rules:standard_package", + "//rules/private:standard_package", # "@bazel_skylib//lib:paths", ], visibility = ["//visibility:public"], diff --git a/examples/policy_checker/license_policy_check.bzl b/examples/policy_checker/license_policy_check.bzl index b91c96f..bb35eee 100644 --- a/examples/policy_checker/license_policy_check.bzl +++ b/examples/policy_checker/license_policy_check.bzl @@ -23,7 +23,7 @@ load( "gather_licenses_info", ) load("@rules_license//rules:providers.bzl", "LicenseInfo") -load("@rules_license//rules:private/gathering_providers.bzl", "TransitiveLicensesInfo") +load("@rules_license//rules/private:gathering_providers.bzl", "TransitiveLicensesInfo") # This is a crude example of the kind of thing which can be done. def _license_policy_check_impl(ctx): diff --git a/rules/compliance.bzl b/rules/compliance.bzl index 2127869..2fb04ab 100644 --- a/rules/compliance.bzl +++ b/rules/compliance.bzl @@ -20,7 +20,7 @@ load( "write_licenses_info", ) load( - "@rules_license//rules:private/gathering_providers.bzl", + "@rules_license//rules/private:gathering_providers.bzl", "TransitiveLicensesInfo", ) diff --git a/rules/gather_licenses_info.bzl b/rules/gather_licenses_info.bzl index 894d20d..9dd1cbc 100644 --- a/rules/gather_licenses_info.bzl +++ b/rules/gather_licenses_info.bzl @@ -20,7 +20,7 @@ load( "should_traverse", ) load( - "@rules_license//rules:private/gathering_providers.bzl", + "@rules_license//rules/private:gathering_providers.bzl", "TransitiveLicensesInfo", ) diff --git a/rules/gather_metadata.bzl b/rules/gather_metadata.bzl index 609af72..162ea97 100644 --- a/rules/gather_metadata.bzl +++ b/rules/gather_metadata.bzl @@ -25,7 +25,7 @@ load( "PackageInfo", ) load( - "@rules_license//rules:private/gathering_providers.bzl", + "@rules_license//rules/private:gathering_providers.bzl", "TransitiveMetadataInfo", ) diff --git a/rules/licenses_core.bzl b/rules/licenses_core.bzl index 46b835c..9bb37cb 100644 --- a/rules/licenses_core.bzl +++ b/rules/licenses_core.bzl @@ -20,7 +20,7 @@ load( "LicenseInfo", ) load( - "@rules_license//rules:private/gathering_providers.bzl", + "@rules_license//rules/private:gathering_providers.bzl", "LicensedTargetInfo", "TransitiveLicensesInfo", ) diff --git a/rules/private/BUILD b/rules/private/BUILD new file mode 100644 index 0000000..452e623 --- /dev/null +++ b/rules/private/BUILD @@ -0,0 +1,35 @@ +# Copyright 2023 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 making license declarations.""" + +package( + default_applicable_licenses = ["//:license"], + default_visibility = ["//visibility:public"], +) + +licenses(["notice"]) + +filegroup( + name = "standard_package", + srcs = glob(["**"]), +) + +# Do not create a bzl_library(). That would create a dependency loop back +# to bazel-skylib. We export the .bzl files to the documentation maker. +exports_files( + glob([ + "*.bzl", + ]), + visibility = ["//doc_build:__pkg__"], +) diff --git a/rules/sbom.bzl b/rules/sbom.bzl index 721999d..73c1861 100644 --- a/rules/sbom.bzl +++ b/rules/sbom.bzl @@ -20,7 +20,7 @@ load( "write_metadata_info", ) load( - "@rules_license//rules:private/gathering_providers.bzl", + "@rules_license//rules/private:gathering_providers.bzl", "TransitiveLicensesInfo", ) -- cgit v1.2.3 From 37f907fc2c8734eab1f4f4859b492675cffeebeb Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Mon, 13 Mar 2023 17:32:29 -0400 Subject: tools --- doc_build/BUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_build/BUILD b/doc_build/BUILD index 87386f1..c50e2d4 100644 --- a/doc_build/BUILD +++ b/doc_build/BUILD @@ -66,7 +66,7 @@ genrule( srcs = ["%s.md" % rule for rule, _ in ORDER], outs = ["reference.md"], cmd = "$(location :merge) $(SRCS) >$@", - exec_tools = [":merge"], + tools = [":merge"], ) [ -- cgit v1.2.3 From 903667be86beb87ac9792ef8d7d065cca03f00d6 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Tue, 21 Mar 2023 15:56:03 -0400 Subject: Allow license_text to be a normal label. Make it easy for someone (who vendors the code) to enable the old behavior. Fixes #68 --- rules/license.bzl | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/rules/license.bzl b/rules/license.bzl index e461388..ea7a586 100644 --- a/rules/license.bzl +++ b/rules/license.bzl @@ -24,6 +24,8 @@ load( "license_rule_impl", ) +_require_license_text_is_a_file = False + _license = rule( implementation = license_rule_impl, attrs = { @@ -34,6 +36,7 @@ _license = rule( " should be listed here. If the user can choose a single one" + " of many, then only list one here.", providers = [LicenseKindInfo], + # This should be the null configuration, not the exec. cfg = "exec", ), "copyright_notice": attr.string( @@ -107,11 +110,12 @@ def license( fail("Can not use both license_kind and license_kinds") 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) + if _require_license_text_is_a_file: + # 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, -- cgit v1.2.3 From 601716257153d373b4545e6fe0a990ac920b351f Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Tue, 21 Mar 2023 16:03:13 -0400 Subject: add a test case --- tests/BUILD | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/BUILD b/tests/BUILD index ab2438f..6ceee9a 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -146,3 +146,17 @@ check_license( ":hello_java", ], ) + + +license( + name = "license_with_generated_text", + license_text = ":created_license", + license_kinds = [":generic_notice_license"], +) + +genrule( + name = "created_license", + outs = ["something.text"], + cmd = "echo hello >$@", +) + -- cgit v1.2.3 From e6d8fbab69597623e499350d548f069ace2b6c29 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Tue, 21 Mar 2023 16:32:58 -0400 Subject: remove duplicate definition of the licnense rule because it is confusing --- rules/license_impl.bzl | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/rules/license_impl.bzl b/rules/license_impl.bzl index 457af13..03477c6 100644 --- a/rules/license_impl.bzl +++ b/rules/license_impl.bzl @@ -45,37 +45,3 @@ def license_rule_impl(ctx): ) _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.", - ), - }, -) -- cgit v1.2.3 From 9e310623d6d539c14a81f026eae3b360e435e5dc Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Tue, 21 Mar 2023 16:56:17 -0400 Subject: add an interesting comment --- rules/license.bzl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rules/license.bzl b/rules/license.bzl index ea7a586..f1711d8 100644 --- a/rules/license.bzl +++ b/rules/license.bzl @@ -26,6 +26,10 @@ load( _require_license_text_is_a_file = False +# This rule must be named "_license" for backwards compatability with older +# or Bazel that checked that name explicitly. See +# https://github.com/bazelbuild/bazel/commit/bbc221f60bc8c9177470529d85c3e47a5d9aaf21 +# TODO(after bazel 7.0 release): Feel free to rename the rule and move. _license = rule( implementation = license_rule_impl, attrs = { -- cgit v1.2.3 From 9a2d1a2877d827cb2d62ca49d1da6c38dacc7733 Mon Sep 17 00:00:00 2001 From: Tony Aiuto Date: Tue, 21 Mar 2023 17:50:46 -0400 Subject: update usage comment --- rules/license.bzl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rules/license.bzl b/rules/license.bzl index f1711d8..032599d 100644 --- a/rules/license.bzl +++ b/rules/license.bzl @@ -24,6 +24,8 @@ load( "license_rule_impl", ) +# Enable this if your organization requires the license text to be a file +# checked into source control instead of, possibly, another rule. _require_license_text_is_a_file = False # This rule must be named "_license" for backwards compatability with older -- cgit v1.2.3