blob: b869314797fda8ae14ed0ccf0141d325602d2ad9 [file] [log] [blame]
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Rules and macros for collecting LicenseInfo providers."""
load(
"@rules_license//rules:providers.bzl",
"LicenseInfo",
"LicensesInfo",
)
# Debugging verbosity
_VERBOSITY = 0
def _debug(loglevel, msg):
if _VERBOSITY > loglevel:
print(msg) # buildifier: disable=print
def _get_transitive_licenses(deps, licenses, trans):
for dep in deps:
if LicenseInfo in dep:
license = dep[LicenseInfo]
_debug(1, " depends on license: %s" % license.rule)
licenses.append(license)
if LicensesInfo in dep:
license_list = dep[LicensesInfo].licenses
if license_list:
_debug(1, " transitively depends on: %s" % licenses)
trans.append(license_list)
def _gather_licenses_info_impl(target, ctx):
licenses = []
trans = []
if hasattr(ctx.rule.attr, "applicable_licenses"):
_get_transitive_licenses(ctx.rule.attr.applicable_licenses, licenses, trans)
if hasattr(ctx.rule.attr, "deps"):
_get_transitive_licenses(ctx.rule.attr.deps, licenses, trans)
if hasattr(ctx.rule.attr, "srcs"):
_get_transitive_licenses(ctx.rule.attr.srcs, licenses, trans)
return [LicensesInfo(licenses = depset(tuple(licenses), transitive = trans))]
gather_licenses_info = aspect(
doc = """Collects LicenseInfo providers into a single LicensesInfo provider.""",
implementation = _gather_licenses_info_impl,
attr_aspects = ["applicable_licenses", "deps", "srcs"],
apply_to_generating_rules = True,
)
def write_licenses_info(ctx, deps, json_out):
"""Writes LicensesInfo providers for a set of targets as JSON.
TODO(aiuto): Document JSON schema.
Usage:
write_licenses_info must be called from a rule implementation, where the
rule has run the gather_licenses_info aspect on its deps to collect the
transitive closure of LicenseInfo providers into a LicenseInfo provider.
foo = rule(
implementation = _foo_impl,
attrs = {
"deps": attr.label_list(aspects = [gather_licenses_info])
}
)
def _foo_impl(ctx):
...
out = ctx.actions.declare_file("%s_licenses.json" % ctx.label.name)
write_licenses_info(ctx, ctx.attr.deps, licenses_file)
Args:
ctx: context of the caller
deps: a list of deps which should have LicensesInfo providers.
This requires that you have run the gather_licenses_info
aspect over them
json_out: output handle to write the JSON info
"""
rule_template = """ {{
"rule": "{rule}",
"license_kinds": [{kinds}
],
"copyright_notice": "{copyright_notice}",
"package_name": "{package_name}",
"license_text": "{license_text}"\n }}"""
kind_template = """
{{
"target": "{kind_path}",
"name": "{kind_name}",
"conditions": {kind_conditions}
}}"""
licenses = []
for dep in deps:
if LicensesInfo in dep:
for license in dep[LicensesInfo].licenses.to_list():
_debug(0, " Requires license: %s" % license)
kinds = []
for kind in license.license_kinds:
kinds.append(kind_template.format(
kind_name = kind.name,
kind_path = kind.label,
kind_conditions = kind.conditions,
))
licenses.append(rule_template.format(
rule = license.rule,
copyright_notice = license.copyright_notice,
package_name = license.package_name,
license_text = license.license_text.path,
kinds = ",\n".join(kinds),
))
ctx.actions.write(
output = json_out,
content = "[\n%s\n]\n" % ",\n".join(licenses),
)