From 37390a81f022ca352d2e2d2aa59446c31761f9c2 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Thu, 1 Aug 2024 11:37:37 +0200 Subject: [PATCH] Add `current_module_package_info` macro This macro makes it easy for Bazel modules to declare a `package_info` without having to provide any information manually. --- BUILD | 6 +- MODULE.bazel | 1 + doc_build/BUILD | 1 + rules/current_module_package_info.bzl | 75 +++++++++++++++++++ tests/current_module_package_info/BUILD.bazel | 5 ++ .../starlark_tests.bzl | 68 +++++++++++++++++ 6 files changed, 152 insertions(+), 4 deletions(-) create mode 100644 rules/current_module_package_info.bzl create mode 100644 tests/current_module_package_info/BUILD.bazel create mode 100644 tests/current_module_package_info/starlark_tests.bzl diff --git a/BUILD b/BUILD index 31520c4..e00158e 100644 --- a/BUILD +++ b/BUILD @@ -13,7 +13,7 @@ # limitations under the License. load("@rules_license//rules:license.bzl", "license") -load("@rules_license//rules:package_info.bzl", "package_info") +load("@rules_license//rules:current_module_package_info.bzl", "current_module_package_info") load("@rules_license//:version.bzl", "version") package( @@ -31,10 +31,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 be953b1..dc10dd2 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -14,4 +14,5 @@ module( # Only for development bazel_dep(name = "rules_pkg", version = "0.7.0", dev_dependency = True) bazel_dep(name = "rules_python", version = "0.23.0", dev_dependency = True) +bazel_dep(name = "rules_testing", version = "0.6.0", dev_dependency = True) bazel_dep(name = "stardoc", version = "0.5.3", dev_dependency = True) diff --git a/doc_build/BUILD b/doc_build/BUILD index 66ce6f7..5b418f1 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