diff --git a/BUILD b/BUILD index 44ada8b..90abe22 100644 --- a/BUILD +++ b/BUILD @@ -13,6 +13,7 @@ # limitations under the License. load("@rules_license//:version.bzl", "version") +load("@rules_license//rules:current_module_package_info.bzl", "current_module_package_info") load("@rules_license//rules:license.bzl", "license") load("@rules_license//rules:package_info.bzl", "package_info") @@ -31,10 +32,8 @@ license( license_text = "LICENSE", ) -package_info( +current_module_package_info( name = "package_info", - package_name = "rules_license", - package_version = version, ) exports_files( diff --git a/MODULE.bazel b/MODULE.bazel index 639c7c3..4783130 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -15,4 +15,5 @@ module( bazel_dep(name = "bazel_skylib", version = "1.7.1", dev_dependency = True) bazel_dep(name = "rules_pkg", version = "1.0.1", dev_dependency = True) bazel_dep(name = "rules_python", version = "0.35.0", dev_dependency = True) +bazel_dep(name = "rules_testing", version = "0.6.0", dev_dependency = True) bazel_dep(name = "stardoc", version = "0.6.2", dev_dependency = True) diff --git a/doc_build/BUILD b/doc_build/BUILD index 2856deb..e6d3862 100644 --- a/doc_build/BUILD +++ b/doc_build/BUILD @@ -62,6 +62,7 @@ ORDER = [ ("gather_metadata_info", "//rules_gathering:gather_metadata.bzl"), ("gather_metadata_info_and_write", "//rules_gathering:gather_metadata.bzl"), ("trace", "//rules_gathering:trace.bzl"), + ("current_module_package_info", "//rules:current_module_package_info.bzl"), ] genrule( diff --git a/rules/current_module_package_info.bzl b/rules/current_module_package_info.bzl new file mode 100644 index 0000000..47a9693 --- /dev/null +++ b/rules/current_module_package_info.bzl @@ -0,0 +1,75 @@ +# Copyright 2024 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:package_info.bzl", + "package_info", +) + +# +# current_module_package_info() +# + +_DEFAULT_REGISTRY = "https://bcr.bazel.build" + +# buildifier: disable=function-docstring-args +def current_module_package_info( + name, + registry = _DEFAULT_REGISTRY, + visibility = ["//:__subpackages__"], + **kwargs): + """A wrapper around package_info with info for the current Bazel module. + + If `//:package_info` is a target of this macro, it can be registered for the + entire module by adding a `REPO.bazel` file with the following content to + the root of the module: + + ``` + repo(default_package_metadata = ["//:package_info"]) + ``` + + @wraps(package_info) + + Args: + name: str target name. + registry: str the URL of the registry hosting the module. + visibility: list[str] visibility of the target. + kwargs: other args. Most are ignored. + """ + + package_name = native.module_name() + package_version = native.module_version() + + normalized_registry = registry.rstrip("/") + package_url = "{registry}/modules/{name}/{version}/source.json".format( + registry = normalized_registry, + name = package_name, + version = package_version, + ) + purl = "pkg:bazel/{name}@{version}{registry_qualifier}".format( + name = package_name, + version = package_version, + registry_qualifier = "" if normalized_registry == _DEFAULT_REGISTRY else "?repository_url=" + normalized_registry, + ) + + package_info( + name = name, + package_name = package_name, + package_url = package_url, + package_version = package_version, + purl = purl, + visibility = visibility, + **kwargs + ) diff --git a/tests/current_module_package_info/BUILD.bazel b/tests/current_module_package_info/BUILD.bazel new file mode 100644 index 0000000..a85d956 --- /dev/null +++ b/tests/current_module_package_info/BUILD.bazel @@ -0,0 +1,5 @@ +load(":starlark_tests.bzl", "starlark_tests") + +starlark_tests( + name = "starlark_tests" +) \ No newline at end of file diff --git a/tests/current_module_package_info/starlark_tests.bzl b/tests/current_module_package_info/starlark_tests.bzl new file mode 100644 index 0000000..efcdbbe --- /dev/null +++ b/tests/current_module_package_info/starlark_tests.bzl @@ -0,0 +1,68 @@ +load("//rules:current_module_package_info.bzl", "current_module_package_info") +load("//rules:providers.bzl", "PackageInfo") +load("//:version.bzl", "version") +load("@rules_testing//lib:analysis_test.bzl", "analysis_test", "test_suite") +load("@rules_testing//lib:truth.bzl", "subjects") + +def _test_current_module_package_info(name): + current_module_package_info( + name = name + "_subject", + ) + analysis_test( + name = name, + impl = _test_current_module_package_info_impl, + target = name + "_subject", + provider_subject_factories = [_package_info_subject_factory], + ) + +def _test_current_module_package_info_impl(env, target): + env.expect.that_target(target).has_provider(PackageInfo) + subject = env.expect.that_target(target).provider(PackageInfo) + subject.package_name().equals("rules_license") + subject.package_url().equals("https://bcr.bazel.build/modules/rules_license/{}/source.json".format(version)) + subject.package_version().equals(version) + subject.purl().equals("pkg:bazel/rules_license@{}".format(version)) + +def _test_current_module_package_info_custom_registry(name): + current_module_package_info( + name = name + "_subject", + registry = "https://example.com/registry/", + ) + analysis_test( + name = name, + impl = _test_current_module_package_info_custom_registry_impl, + target = name + "_subject", + provider_subject_factories = [_package_info_subject_factory], + ) + +def _test_current_module_package_info_custom_registry_impl(env, target): + env.expect.that_target(target).has_provider(PackageInfo) + subject = env.expect.that_target(target).provider(PackageInfo) + subject.package_name().equals("rules_license") + subject.package_url().equals("https://example.com/registry/modules/rules_license/{}/source.json".format(version)) + subject.package_version().equals(version) + subject.purl().equals("pkg:bazel/rules_license@{}?repository_url=https://example.com/registry".format(version)) + +_package_info_subject_factory = struct( + type = PackageInfo, + name = "PackageInfo", + factory = lambda actual, *, meta: subjects.struct( + actual, + meta = meta, + attrs = { + "package_name": subjects.str, + "package_url": subjects.str, + "package_version": subjects.str, + "purl": subjects.str, + }, + ), +) + +def starlark_tests(name): + test_suite( + name = name, + tests = [ + _test_current_module_package_info, + _test_current_module_package_info_custom_registry, + ], + ) \ No newline at end of file