From 61c4add8386cd547131338c1b4879a9d877aa20e Mon Sep 17 00:00:00 2001 From: Amelia <140295624+AmeliasCode@users.noreply.github.com> Date: Fri, 20 Dec 2024 15:09:32 -0800 Subject: [PATCH] Allow rules_license metadata in bzlmod --- .bazelci/presubmit.yml | 8 +++ crate_universe/extensions.bzl | 7 +++ .../bzlmod/rules_license_metadata/.bazelrc | 12 ++++ .../rules_license_metadata/.bazelversion | 1 + .../bzlmod/rules_license_metadata/.gitignore | 2 + .../bzlmod/rules_license_metadata/BUILD.bazel | 50 ++++++++++++++++ .../bzlmod/rules_license_metadata/Cargo.lock | 16 +++++ .../bzlmod/rules_license_metadata/Cargo.toml | 12 ++++ .../rules_license_metadata/MODULE.bazel | 54 +++++++++++++++++ .../rules_license_metadata/WORKSPACE.bzlmod | 1 + .../license_check_test.py | 60 +++++++++++++++++++ .../bzlmod/rules_license_metadata/src/main.rs | 18 ++++++ 12 files changed, 241 insertions(+) create mode 100644 examples/bzlmod/rules_license_metadata/.bazelrc create mode 120000 examples/bzlmod/rules_license_metadata/.bazelversion create mode 100644 examples/bzlmod/rules_license_metadata/.gitignore create mode 100644 examples/bzlmod/rules_license_metadata/BUILD.bazel create mode 100644 examples/bzlmod/rules_license_metadata/Cargo.lock create mode 100644 examples/bzlmod/rules_license_metadata/Cargo.toml create mode 100644 examples/bzlmod/rules_license_metadata/MODULE.bazel create mode 100644 examples/bzlmod/rules_license_metadata/WORKSPACE.bzlmod create mode 100644 examples/bzlmod/rules_license_metadata/license_check_test.py create mode 100644 examples/bzlmod/rules_license_metadata/src/main.rs diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index d7cac2dfcf..479836976f 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -846,6 +846,14 @@ tasks: working_directory: examples/bzlmod/hello_world_no_cargo build_targets: - "//..." + bzlmod_rules_license_metadata: + name: rules_license_metadata with bzlmod + platform: ubuntu2004 + working_directory: examples/bzlmod/rules_license_metadata + build_targets: + - "//..." + test_targets: + - "//..." bzlmod_override_targets: name: Override Targets bzlmod platform: ubuntu2004 diff --git a/crate_universe/extensions.bzl b/crate_universe/extensions.bzl index a224cc1224..f52cf3b90f 100644 --- a/crate_universe/extensions.bzl +++ b/crate_universe/extensions.bzl @@ -25,6 +25,8 @@ _OPT_BOOL_VALUES = { "on": True, } +GENERATE_RULES_LICENSE_METADATA_ATTR = attr.bool(doc = "Generate rules_license metadata for third party crates", default = False) + def _get_or_insert(d, key, value): if key not in d: d[key] = value @@ -78,6 +80,7 @@ def _generate_hub_and_spokes(*, module_ctx, cargo_bazel, cfg, annotations, cargo rendering_config = json.decode(render_config( regen_command = "Run 'cargo update [--workspace]'", + generate_rules_license_metadata = cfg.generate_rules_license_metadata, )) config_file = tag_path.get_child("config.json") module_ctx.file( @@ -512,6 +515,8 @@ _from_cargo = tag_class( "generate_build_scripts": CRATES_VENDOR_ATTRS["generate_build_scripts"], "splicing_config": CRATES_VENDOR_ATTRS["splicing_config"], "supported_platform_triples": CRATES_VENDOR_ATTRS["supported_platform_triples"], + } | { + "generate_rules_license_metadata": GENERATE_RULES_LICENSE_METADATA_ATTR, }, ) @@ -653,6 +658,8 @@ _from_specs = tag_class( "generate_build_scripts": CRATES_VENDOR_ATTRS["generate_build_scripts"], "splicing_config": CRATES_VENDOR_ATTRS["splicing_config"], "supported_platform_triples": CRATES_VENDOR_ATTRS["supported_platform_triples"], + } | { + "generate_rules_license_metadata": GENERATE_RULES_LICENSE_METADATA_ATTR, }, ) diff --git a/examples/bzlmod/rules_license_metadata/.bazelrc b/examples/bzlmod/rules_license_metadata/.bazelrc new file mode 100644 index 0000000000..f9d474e02f --- /dev/null +++ b/examples/bzlmod/rules_license_metadata/.bazelrc @@ -0,0 +1,12 @@ +# Required on windows +common --enable_platform_specific_config +startup --windows_enable_symlinks +build:windows --enable_runfiles + +build --experimental_enable_bzlmod + +# This isn't currently the defaut in Bazel, but we enable it to test we'll be ready if/when it flips. +build --incompatible_disallow_empty_glob + +# Required for cargo_build_script support before Bazel 7 +build --incompatible_merge_fixed_and_default_shell_env diff --git a/examples/bzlmod/rules_license_metadata/.bazelversion b/examples/bzlmod/rules_license_metadata/.bazelversion new file mode 120000 index 0000000000..b332604979 --- /dev/null +++ b/examples/bzlmod/rules_license_metadata/.bazelversion @@ -0,0 +1 @@ +../.bazelversion \ No newline at end of file diff --git a/examples/bzlmod/rules_license_metadata/.gitignore b/examples/bzlmod/rules_license_metadata/.gitignore new file mode 100644 index 0000000000..e28b710ff7 --- /dev/null +++ b/examples/bzlmod/rules_license_metadata/.gitignore @@ -0,0 +1,2 @@ +/bazel-* +.DS_Store \ No newline at end of file diff --git a/examples/bzlmod/rules_license_metadata/BUILD.bazel b/examples/bzlmod/rules_license_metadata/BUILD.bazel new file mode 100644 index 0000000000..9f8903d2a2 --- /dev/null +++ b/examples/bzlmod/rules_license_metadata/BUILD.bazel @@ -0,0 +1,50 @@ +load("@crates//:defs.bzl", "all_crate_deps") +load("@crates_from_spec//:defs.bzl", all_crate_deps_from_spec = "all_crate_deps") +load("@rules_license//sample_reports:licenses_used.bzl", "licenses_used") +load("@rules_python//python:defs.bzl", "py_test") +load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_doc") + +package(default_visibility = ["//visibility:public"]) + +rust_binary( + name = "all_crate_deps", + srcs = ["src/main.rs"], + deps = all_crate_deps(normal = True), +) + +rust_binary( + name = "all_crate_deps_from_spec", + srcs = ["src/main.rs"], + deps = all_crate_deps_from_spec(normal = True), +) + +rust_doc( + name = "all_crate_deps_doc", + crate = ":all_crate_deps", +) + +licenses_used( + name = "licenses_used", + out = "license_info.json", + deps = [ + ":all_crate_deps", + ], +) + +licenses_used( + name = "licenses_used_from_spec", + out = "license_info_from_spec.json", + deps = [ + ":all_crate_deps_from_spec", + ], +) + +py_test( + name = "license_check_test", + srcs = ["license_check_test.py"], + data = [ + ":license_info.json", + ":license_info_from_spec.json", + ], + python_version = "PY3", +) diff --git a/examples/bzlmod/rules_license_metadata/Cargo.lock b/examples/bzlmod/rules_license_metadata/Cargo.lock new file mode 100644 index 0000000000..3471b64a53 --- /dev/null +++ b/examples/bzlmod/rules_license_metadata/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "all_crate_deps" +version = "0.0.0" +dependencies = [ + "anyhow", +] + +[[package]] +name = "anyhow" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" diff --git a/examples/bzlmod/rules_license_metadata/Cargo.toml b/examples/bzlmod/rules_license_metadata/Cargo.toml new file mode 100644 index 0000000000..ec99a91124 --- /dev/null +++ b/examples/bzlmod/rules_license_metadata/Cargo.toml @@ -0,0 +1,12 @@ +[workspace] +[package] +name = "all_crate_deps" +version = "0.0.0" +edition = "2021" +publish = false + +[lib] +path = "fake.rs" + +[dependencies] +anyhow = "1.0.79" diff --git a/examples/bzlmod/rules_license_metadata/MODULE.bazel b/examples/bzlmod/rules_license_metadata/MODULE.bazel new file mode 100644 index 0000000000..00227ece30 --- /dev/null +++ b/examples/bzlmod/rules_license_metadata/MODULE.bazel @@ -0,0 +1,54 @@ +"""bazelbuild/rules_rust - bzlmod example""" + +module( + name = "rules_license_metadata_bzlmod_example", + version = "0.0.0", +) + +bazel_dep(name = "platforms", version = "0.0.10") +bazel_dep( + name = "bazel_skylib", + version = "1.7.1", +) +bazel_dep(name = "rules_python", version = "1.0.0") +bazel_dep(name = "rules_license", version = "1.0.0") +bazel_dep( + name = "rules_rust", + version = "0.0.0", +) +local_path_override( + module_name = "rules_rust", + path = "../../..", +) + +rust = use_extension("@rules_rust//rust:extensions.bzl", "rust") +rust.toolchain(edition = "2021") +use_repo( + rust, + "rust_toolchains", +) + +register_toolchains("@rust_toolchains//:all") + +crate = use_extension( + "@rules_rust//crate_universe:extensions.bzl", + "crate", +) +crate.from_cargo( + name = "crates", + cargo_lockfile = "//:Cargo.lock", + generate_rules_license_metadata = True, + manifests = ["//:Cargo.toml"], +) +use_repo(crate, "crates") + +crate_from_spec = use_extension("@rules_rust//crate_universe:extensions.bzl", "crate") +crate_from_spec.spec( + package = "anyhow", + version = "1.0.79", +) +crate_from_spec.from_specs( + name = "crates_from_spec", + generate_rules_license_metadata = True, +) +use_repo(crate_from_spec, "crates_from_spec") diff --git a/examples/bzlmod/rules_license_metadata/WORKSPACE.bzlmod b/examples/bzlmod/rules_license_metadata/WORKSPACE.bzlmod new file mode 100644 index 0000000000..8e081c0b59 --- /dev/null +++ b/examples/bzlmod/rules_license_metadata/WORKSPACE.bzlmod @@ -0,0 +1 @@ +# Intentionally blank; enable strict mode for bzlmod diff --git a/examples/bzlmod/rules_license_metadata/license_check_test.py b/examples/bzlmod/rules_license_metadata/license_check_test.py new file mode 100644 index 0000000000..6189372b81 --- /dev/null +++ b/examples/bzlmod/rules_license_metadata/license_check_test.py @@ -0,0 +1,60 @@ +import json +import os +import codecs +import unittest + +# Largely borrowed from rules_license: +# https://github.com/bazelbuild/rules_license/blob/main/examples/vndor/constant_gen/verify_licenses_test.py + +APACHE_2_LICENSE_TARGET = "@rules_license~//licenses/spdx:Apache-2.0" +MIT_LICENSE_TARGET = "@rules_license~//licenses/spdx:MIT" + +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()) + +class LicenseCheckTest(unittest.TestCase): + def test_license_info(self): + self.check_license("license_info.json", "//:all_crate_deps") + + def test_license_info_from_spec(self): + self.check_license("license_info_from_spec.json", "//:all_crate_deps_from_spec") + + + # Load the generated report and ensure that the data is as expected. + # Note: anyhow@v1.0.79 is licensed under both Apache-2.0 and MIT, so we expect both to be present + # If this dependency is ever changed, you may need to update this test to reflect that + def check_license(self, file_name, top_level_target): + info = load_licenses_info(os.path.join(os.path.dirname(__file__), file_name)) + self.assertEqual(len(info),1) + all_crate_deps = info[0] + self.assertEqual(all_crate_deps["top_level_target"], top_level_target) + self.assertEqual(len(all_crate_deps["dependencies"]), 3) + + licenses = all_crate_deps["licenses"] + + self.assertEqual(len(licenses), 1) + + apache_found = False + mit_found = False + other_found = False + other_license_found = "" + for license_item in licenses: + for kind in license_item["license_kinds"]: + if kind["target"] == APACHE_2_LICENSE_TARGET: + apache_found = True + continue + if kind["target"] == MIT_LICENSE_TARGET: + mit_found = True + continue + else: + other_found = True + other_license_found = kind["target"] + continue + self.assertFalse(other_found, "Unexpected license found: (%s)." % other_license_found) + self.assertTrue(apache_found, "Apache-2.0 license not found.") + self.assertTrue(mit_found, "MIT license not found.") + +if __name__ == "__main__": + unittest.main(verbosity=3) \ No newline at end of file diff --git a/examples/bzlmod/rules_license_metadata/src/main.rs b/examples/bzlmod/rules_license_metadata/src/main.rs new file mode 100644 index 0000000000..1aa867e38a --- /dev/null +++ b/examples/bzlmod/rules_license_metadata/src/main.rs @@ -0,0 +1,18 @@ +// Copyright 2015 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. + +fn main() -> anyhow::Result<()> { + println!("Hello, world!"); + Ok(()) +}