// Copyright 2016 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. package com.google.devtools.build.lib.analysis; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.testing.EqualsTester; import com.google.devtools.build.lib.analysis.LicensesProvider.TargetLicense; import com.google.devtools.build.lib.analysis.util.AnalysisMock; import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.packages.License; import com.google.devtools.build.lib.packages.License.DistributionType; import com.google.devtools.build.lib.packages.License.LicenseParsingException; import java.util.Arrays; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Integration tests for {@link License}. */ @RunWith(JUnit4.class) public class LicensingTests extends BuildViewTestCase { /** Filter to remove implicit dependencies of sh_* rules. */ private static final Predicate<String> SH_LABEL_NAME_FILTER = new Predicate<String>() { @Override public boolean apply(String label) { return !label.startsWith("//util/shell/"); } }; private static final Predicate<Label> SH_LABEL_FILTER = new Predicate<Label>() { @Override public boolean apply(Label label) { return SH_LABEL_NAME_FILTER.apply("//" + label.getPackageName()); } }; /** Filter to remove implicit dependencies of Java rules. */ private static final Predicate<String> JAVA_LABEL_NAME_FILTER = new Predicate<String>() { @Override public boolean apply(String label) { return !label.startsWith("//third_party/java/jdk"); } }; public static final Predicate<Label> JAVA_LABEL_FILTER = new Predicate<Label>() { @Override public boolean apply(Label label) { return JAVA_LABEL_NAME_FILTER.apply("//" + label.getPackageName()); } }; @SuppressWarnings("unchecked") public static final Predicate<Label> CC_OR_JAVA_OR_SH_LABEL_FILTER = Predicates.and( AnalysisMock.get().ccSupport().labelFilter(), JAVA_LABEL_FILTER, SH_LABEL_FILTER); @Before public final void useCorrectConfiguration() throws Exception { useConfiguration("--check_licenses"); } @Test public void testCollectDistribs() throws Exception { scratch.file("a/BUILD", "distribs(['client', 'web'])", "cc_library(name = 'alib', srcs=['a.cc'], deps=[':blib'])", "cc_library(name = 'blib', distribs=['client'])"); scratch.file("kaboom/BUILD", "cc_library(name = 'boom', srcs=['bang.cc'])"); assertThat(getTarget("//a:alib").getDistributions()) .containsExactlyElementsIn( Sets.newHashSet(License.DistributionType.CLIENT, License.DistributionType.WEB)); assertThat(getTarget("//a:blib").getDistributions()) .containsExactlyElementsIn(Sets.newHashSet(License.DistributionType.CLIENT)); assertThat(getTarget("//kaboom:boom").getDistributions()) .containsExactlyElementsIn(Sets.newHashSet(License.DistributionType.INTERNAL)); } // Same test, using package.distribs. @Test public void testCollectDistribsPackage() throws Exception { scratch.file("a/BUILD", "package(distribs = ['client', 'web'])", "cc_library(name = 'alib', srcs=['a.cc'], deps=[':blib'])", "cc_library(name = 'blib', distribs=['client'])"); scratch.file("kaboom/BUILD", "cc_library(name = 'boom', srcs=['bang.cc'])"); assertThat(getTarget("//a:alib").getDistributions()) .containsExactlyElementsIn( Sets.newHashSet(License.DistributionType.CLIENT, License.DistributionType.WEB)); assertThat(getTarget("//a:blib").getDistributions()) .containsExactlyElementsIn(Sets.newHashSet(License.DistributionType.CLIENT)); assertThat(getTarget("//kaboom:boom").getDistributions()) .containsExactlyElementsIn(Sets.newHashSet(License.DistributionType.INTERNAL)); } // Test for package.licenses @Test public void testCollectLicensesPackage() throws Exception { scratch.file("a/BUILD", "licenses(['restricted', 'reciprocal'])", "exports_files(['a'])"); ConfiguredTarget target = getConfiguredTarget("//a:a"); License license = getTarget(target.getLabel()).getLicense(); assertEquals(License.parseLicense(Arrays.asList("restricted", "reciprocal")), license); } @Test public void testCollectLicenses() throws Exception { Predicate<Label> SOURCE_FILTER = new Predicate<Label>() { @Override public boolean apply(Label label) { // Remove source files from the results. return AnalysisMock.get().ccSupport().labelFilter().apply(label) && !label.toString().endsWith(".cc"); } }; scratch.file("a/BUILD", "licenses(['restricted', 'reciprocal'])", "cc_library(name = 'alib', srcs=['a.cc'], deps=[':blib'])", "cc_library(name = 'blib', srcs=['b.cc'], distribs=['web'], " + "deps=['//kaboom:boom', '//kablam:blam'])", "exports_files(['c'])"); scratch.file("kaboom/BUILD", "licenses(['notice'])", "cc_library(name = 'boom', srcs=['bang.cc'])"); scratch.file("kablam/BUILD", "licenses(['restricted','exception=//a:alib'])", "cc_library(name='blam', srcs=['blam.cc'])"); ConfiguredTarget aTarget = getConfiguredTarget("//a:alib"); ConfiguredTarget bTarget = getConfiguredTarget("//a:blib"); ConfiguredTarget cTarget = getConfiguredTarget("//a:c"); ConfiguredTarget kaboomTarget = getConfiguredTarget("//kaboom:boom"); ConfiguredTarget kablamTarget = getConfiguredTarget("//kablam:blam"); Map<Label, License> aMap = Maps.filterKeys(getTransitiveLicenses(aTarget), SOURCE_FILTER); Map<Label, License> aExpected = licenses("//a:alib", "restricted,reciprocal", "//a:blib", "restricted,reciprocal", "//kablam:blam", "restricted,exception=//a:alib", "//kaboom:boom", "notice"); assertSameMapEntries(aExpected, aMap); Map<Label, License> bMap = Maps.filterKeys(getTransitiveLicenses(bTarget), SOURCE_FILTER); Map<Label, License> bExpected = licenses("//a:blib", "restricted,reciprocal", "//kablam:blam", "restricted,exception=//a:alib", "//kaboom:boom", "notice"); assertSameMapEntries(bExpected, bMap); Map<Label, License> kaboomMap = Maps.filterKeys(getTransitiveLicenses(kaboomTarget), SOURCE_FILTER); assertSameMapEntries( licenses("//kaboom:boom", "notice"), kaboomMap); Map<Label, License> kablamMap = Maps.filterKeys(getTransitiveLicenses(kablamTarget), SOURCE_FILTER); assertSameMapEntries( licenses("//kablam:blam", "restricted,exception=//a:alib"), kablamMap); License cLicense = getTarget(cTarget.getLabel()).getLicense(); assertEquals(License.parseLicense(Arrays.asList("restricted", "reciprocal")), cLicense); } @Test public void testClientLicenseOverridesDefault() throws Exception { scratch.file("a/BUILD", "licenses(['restricted', 'reciprocal'])", "cc_library(name = 'alib', srcs=['a.cc'], deps=[':blib'], licenses=['unencumbered'])", "cc_library(name = 'blib', srcs=['b.cc'], distribs=['web'])", "exports_files(['c'], licenses = ['unencumbered'])"); ConfiguredTarget aTarget = getConfiguredTarget("//a:alib"); ConfiguredTarget bTarget = getConfiguredTarget("//a:blib"); ConfiguredTarget cTarget = getConfiguredTarget("//a:c"); License aLicense = getTarget(aTarget.getLabel()).getLicense(); License bLicense = getTarget(bTarget.getLabel()).getLicense(); License cLicense = getTarget(cTarget.getLabel()).getLicense(); assertFalse(aLicense.equals(bLicense)); assertEquals(License.parseLicense(Arrays.asList("unencumbered")), aLicense); assertEquals(License.parseLicense(Arrays.asList("restricted", "reciprocal")), bLicense); assertEquals(License.parseLicense(Arrays.asList("unencumbered")), cLicense); } @Test public void testNoneLicenseHandledCorrectly() throws Exception { scratch.file("music/BUILD", "sh_binary(name='piano', srcs=['piano.sh'], licenses=['none'])"); Map<Label, License> licenses = Maps.filterKeys( getTransitiveLicenses(getConfiguredTarget("//music:piano")), CC_OR_JAVA_OR_SH_LABEL_FILTER); assertThat(licenses).isEmpty(); } @Test public void testToolLicenseAddedCorrectly() throws Exception { scratch.file("powertools/BUILD", "sh_binary(name = 'chainsaw',", " srcs = ['chainsaw.sh'],", " licenses=['restricted'],", " output_licenses=['unencumbered'])"); scratch.file("gr/BUILD", "licenses(['unencumbered'])", "genrule(name = 'lumberjack',", " outs=['firewood'],", " tools=['//powertools:chainsaw'],", " cmd='')", "genrule(name = 'xorn',", " outs=['jewel'],", " srcs=['//powertools:chainsaw'],", " cmd='')"); ConfiguredTarget lumberjack = getConfiguredTarget("//gr:lumberjack"); ConfiguredTarget xorn = getConfiguredTarget("//gr:xorn"); Map<Label, License> lumberjackActual = Maps.filterKeys(getTransitiveLicenses(lumberjack), CC_OR_JAVA_OR_SH_LABEL_FILTER); Map<Label, License> xornActual = Maps.filterKeys(getTransitiveLicenses(xorn), CC_OR_JAVA_OR_SH_LABEL_FILTER); Map<Label, License> lumberjackExpected = licenses( "//powertools:chainsaw", "unencumbered", "//gr:lumberjack", "unencumbered"); Map<Label, License> xornExpected = licenses( "//powertools:chainsaw", "restricted", "//gr:xorn", "unencumbered"); assertSameMapEntries(lumberjackExpected, lumberjackActual); assertSameMapEntries(xornExpected, xornActual); } @Test public void testToolLicenseDoesNotIncludeDependencies() throws Exception { scratch.file("powertools/BUILD", "sh_binary(name = 'chainsaw',", " srcs = ['chainsaw.sh'],", " deps = [':gasoline'],", " licenses = ['restricted'],", " output_licenses=['unencumbered'])", "sh_binary(name = 'gasoline',", " srcs = ['gasoline.sh'],", " licenses = ['notice'])"); scratch.file("gr/BUILD", "licenses(['unencumbered'])", "genrule(name = 'lumberjack',", " outs=['firewood'],", " tools=['//powertools:chainsaw'],", " cmd='')"); ConfiguredTarget lumberjack = getConfiguredTarget("//gr:lumberjack"); Map<Label, License> actual = Maps.filterKeys(getTransitiveLicenses(lumberjack), CC_OR_JAVA_OR_SH_LABEL_FILTER); Map<Label, License> expected = licenses( "//powertools:chainsaw", "unencumbered", "//gr:lumberjack", "unencumbered"); assertSameMapEntries(expected, actual); } @Test public void testFilegroupAllowsOutputLicenseDeclaration() throws Exception { scratch.file("apple/BUILD", "licenses(['restricted'])", "filegroup(name='apples',", " output_licenses=['unencumbered'],", " srcs=['rambo', 'granny_smith'])"); scratch.file("apfelmuss/BUILD", "licenses(['unencumbered'])", "genrule(name = 'apfelmuss',", " outs = ['jar'],", " tools = ['//apple:apples'],", " cmd = 'magic')"); ConfiguredTarget apfelmuss = getConfiguredTarget("//apfelmuss:apfelmuss"); Map<Label, License> actual = Maps.filterKeys(getTransitiveLicenses(apfelmuss), CC_OR_JAVA_OR_SH_LABEL_FILTER); Map<Label, License> expected = licenses( "//apfelmuss:apfelmuss", "unencumbered", "//apple:apples", "unencumbered"); assertSameMapEntries(expected, actual); } @Test public void testEmptyToolLicenseWorks() throws Exception { scratch.file("powertools/BUILD", "sh_binary(name = 'chainsaw',", " srcs = ['chainsaw.sh'],", " deps = [':gasoline'],", " licenses = ['restricted'],", " output_licenses=[])", "sh_binary(name = 'gasoline',", " srcs = ['gasoline.sh'],", " licenses = ['notice'])"); scratch.file("gr/BUILD", "genrule(name = 'lumberjack',", " outs=['firewood'],", " tools=['//powertools:chainsaw'],", " cmd='')"); ConfiguredTarget lumberjack = getConfiguredTarget("//gr:lumberjack"); Map<Label, License> actual = Maps.filterKeys(getTransitiveLicenses(lumberjack), CC_OR_JAVA_OR_SH_LABEL_FILTER); Map<Label, License> expected = licenses("//powertools:chainsaw", ""); assertSameMapEntries(expected, actual); } @Test public void testToolLicenseIncludesDependenciesIfNoOutputLicense() throws Exception { scratch.file("powertools/BUILD", "sh_binary(name = 'chainsaw',", " srcs = ['chainsaw.sh'],", " deps = [':gasoline'],", " licenses = ['restricted'])", "sh_binary(name = 'gasoline',", " srcs = ['gasoline.sh'],", " licenses = ['notice'])"); scratch.file("gr/BUILD", "licenses(['unencumbered'])", "genrule(name = 'lumberjack',", " outs=['firewood'],", " tools=['//powertools:chainsaw'],", " cmd='')"); ConfiguredTarget lumberjack = getConfiguredTarget("//gr:lumberjack"); Map<Label, License> actual = Maps.filterKeys(getTransitiveLicenses(lumberjack), CC_OR_JAVA_OR_SH_LABEL_FILTER); Map<Label, License> expected = licenses( "//powertools:chainsaw", "restricted", "//powertools:gasoline", "notice", "//gr:lumberjack", "unencumbered"); assertSameMapEntries(expected, actual); } @Test public void testToolLicenseRecursively() throws Exception { scratch.file("powertools/BUILD", "licenses(['restricted'])", "filegroup(name = 'chainsaw',", " srcs = ['chainsaw.sh'],", " licenses=['restricted'],", " output_licenses=['unencumbered'])", "filegroup(name = 'forward',", " srcs = [':chainsaw'])"); scratch.file("gr/BUILD", "licenses(['unencumbered'])", "genrule(name = 'lumberjack',", " outs=['firewood'],", " tools=['//powertools:forward'],", " cmd='')"); ConfiguredTarget lumberjack = getConfiguredTarget("//gr:lumberjack"); Map<Label, License> lumberjackActual = Maps.filterKeys(getTransitiveLicenses(lumberjack), CC_OR_JAVA_OR_SH_LABEL_FILTER); Map<Label, License> lumberjackExpected = licenses( "//powertools:chainsaw", "unencumbered", "//powertools:forward", "restricted", "//gr:lumberjack", "unencumbered"); assertSameMapEntries(lumberjackExpected, lumberjackActual); } @Test public void testHostAttributeCanBeTool() throws Exception { scratch.file( "test/BUILD", "genrule(", " name='gen',", " outs=['hello.txt'],", " cmd='exit 1',", " tools=['//test:compiler'],", ")", "sh_binary(", " name='compiler',", " srcs=['compiler.sh'],", " licenses=['restricted'],", " output_licenses=['notice'],", ")"); ConfiguredTarget target = getConfiguredTarget("//test:gen"); Map<Label, License> actual = Maps.filterKeys(getTransitiveLicenses(target), CC_OR_JAVA_OR_SH_LABEL_FILTER); Map<Label, License> expected = licenses("//test:compiler", "notice"); assertSameMapEntries(expected, actual); } @Test public void testNonHostAttributeCannotBeTool() throws Exception { scratch.file("keyboardmonkeys/BUILD", "genrule(name='million_monkeys',", " licenses=['restricted'],", " output_licenses=['notice'],", " outs=['source.sh'],", " cmd='type furiously on typewriters')"); scratch.file("product/BUILD", "sh_binary(name='product',", " srcs=['//keyboardmonkeys:source.sh'])"); ConfiguredTarget product = getConfiguredTarget("//product:product"); Map<Label, License> actual = Maps.filterKeys( getTransitiveLicenses(product), CC_OR_JAVA_OR_SH_LABEL_FILTER); Map<Label, License> expected = licenses("//keyboardmonkeys:million_monkeys", "restricted"); assertSameMapEntries(expected, actual); } @Test public void testCcToolchainLicenseOverride() throws Exception { scratch.file("c/BUILD", "filegroup(name = 'dynamic-runtime-libs-cherry', srcs = [], licenses = ['restricted'])", "cc_toolchain(", " name = 'c',", " output_licenses = ['notice'],", " cpu = 'cherry',", " compiler_files = 'compile-cherry',", " dwp_files = 'dwp-cherry',", " coverage_files = 'gcov-cherry',", " linker_files = 'link-cherry',", " strip_files = ':every-file',", " objcopy_files = 'objcopy-cherry',", " all_files = ':every-file',", " dynamic_runtime_libs = ['dynamic-runtime-libs-cherry'],", " static_runtime_libs = ['static-runtime-libs-cherry'])"); ConfiguredTarget target = getConfiguredTarget("//c:c"); Map<Label, License> expected = licenses("//c:c", "notice"); Map<Label, License> actual = getTransitiveLicenses(target); assertSameMapEntries(expected, actual); } @Test public void testLicenseParsing() throws Exception { License l1 = License.parseLicense(Arrays.asList("restricted", "exception=//a:a")); License l2 = License.parseLicense(Arrays.asList("restricted", "reciprocal", "exception=//a:a")); License l3 = License.parseLicense(Arrays.asList("by_exception_only", "reciprocal", "exception=//a:a", "exception=//b:b")); assertEquals("[restricted] with exceptions [//a:a]", l1.toString()); assertEquals("[restricted, reciprocal] with exceptions [//a:a]", l2.toString()); assertEquals("[by_exception_only, reciprocal] with exceptions [//a:a, //b:b]", l3.toString()); } /** * Takes a list of strings, which alternate labels with license lists, and * returns a map of labels to licenses. The license strings are comma-separated * strings. */ protected static Map<Label, License> licenses(String... strings) throws LicenseParsingException, LabelSyntaxException { Map<Label, License> result = new HashMap<>(); for (int i = 0; i < strings.length; i += 2) { String labelStr = strings[i]; String licStr = strings[i + 1]; Label label = Label.parseAbsolute(labelStr); List<String> splitLicenses = licStr.isEmpty() ? Arrays.<String>asList() : Arrays.asList(licStr.split(",")); License license = License.parseLicense(splitLicenses); result.put(label, license); } return result; } protected static<K, V> void assertSameMapEntries(Map<K, V> expected, Map<K, V> actual) { if (expected.size() != actual.size()) { StringBuilder actualString = new StringBuilder(); for (K key : actual.keySet()) { actualString.append(key + ": " + actual.get(key) + "\n"); } int expectedSize = expected.size(); assertWithMessage("Expected map of size '%s' but found map of size '%s', actual value:\n%s", expectedSize, actual.size(), actualString) .that(actual) .hasSize(expectedSize); } for (K key : expected.keySet()) { V expectedValue = expected.get(key); V actualValue = actual.get(key); assertWithMessage("Key '%s'", key).that(actualValue).isNotNull(); assertEquals("Values for key '" + key + "' should match", expectedValue, actualValue); } } @Test public void testDualLicensedUsesLeastRestrictive() throws Exception { scratch.file("user/BUILD", "sh_binary(name = 'user',", " srcs = ['user.sh'],", " deps = ['//used'])"); scratch.file("used/BUILD", "licenses(['restricted', 'permissive'])", "sh_library(name = 'used',", " srcs=['used.sh'])"); ConfiguredTarget used = getConfiguredTarget("//used"); Map<Label, License> usedActual = Maps.filterKeys(getTransitiveLicenses(used), AnalysisMock.get().ccSupport().labelFilter()); Label usedLabel = Label.parseAbsolute("//used"); License license = usedActual.get(usedLabel); license.checkCompatibility(EnumSet.of(DistributionType.CLIENT), getTarget("//user"), usedLabel, reporter, false); assertNoEvents(); } /** Regression test for b/9811855. */ @Test public void testFilegroupLicenses() throws Exception { scratch.file("user/BUILD", "sh_binary(name = 'user',", " srcs = ['user.sh'],", " deps = ['//used'])"); scratch.file("used/BUILD", "licenses(['restricted'])", "filegroup(name = 'used',", " licenses=['unencumbered'],", " srcs=['used.sh'])"); // used.sh is not 'restricted' ConfiguredTarget used = getConfiguredTarget("//used"); Map<Label, License> usedActual = Maps.filterKeys(getTransitiveLicenses(used), AnalysisMock.get().ccSupport().labelFilter()); Label usedLabel = Label.parseAbsolute("//used"); License license = usedActual.get(usedLabel); license.checkCompatibility(EnumSet.of(DistributionType.CLIENT), getTarget("//user"), usedLabel, reporter, false); assertNoEvents(); } /** * Regression test for b/4159051: "Latest blaze release broke --check_licenses" */ @Test public void testLicenseCheckingTakesOnlyOneSelectBranch() throws Exception { scratch.file("eden/BUILD", "config_setting(", " name = 'config_a',", " values = {'define': 'mode=a'})", "cc_library(name = 'eve',", " deps = select({", " ':config_a': ['//third_party/fruit:peach'],", " '//conditions:default': ['//third_party/fruit:apple'],", " }),", " distribs = ['client'])"); scratch.file("third_party/fruit/BUILD", "cc_library(name='apple', licenses=['restricted'])", "cc_library(name='peach', licenses=['unencumbered'])"); useConfiguration("--check_licenses", "--define", "mode=a"); ConfiguredTarget eve = getConfiguredTarget("//eden:eve"); Map<Label, License> eveActual = Maps.filterKeys(getTransitiveLicenses(eve), AnalysisMock.get().ccSupport().labelFilter()); Map<Label, License> eveExpected = licenses("//third_party/fruit:peach", "unencumbered"); assertSameMapEntries(eveExpected, eveActual); assertNoEvents(); } @Test public void configSettingsHaveNoLicense() throws Exception { scratch.file("third_party/restricted/BUILD", "licenses(['by_exception_only'])", "config_setting(", " name = 'a_setting',", " values = {'define': 'mode=a'})"); useConfiguration("--check_licenses", "--define", "mode=a"); ConfiguredTarget configSetting = getConfiguredTarget("//third_party/restricted:a_setting"); assertThat(configSetting.getProvider(LicensesProvider.class).getTransitiveLicenses()) .isEmpty(); } @Test public void testJavaToolchainOutputLicense() throws Exception { scratch.file("java/a/BUILD", "java_toolchain(", " name = 'toolchain',", " licenses = ['restricted'],", " output_licenses = ['unencumbered'],", " encoding = 'UTF-8',", " source_version = '8',", " target_version = '8',", " bootclasspath = [':bcp'],", " extclasspath = [':ecp'],", " javac = [':langtools'],", " javabuilder = ['javabuilder'],", " header_compiler = ['header_compiler'],", " singlejar = ['singlejar'],", " genclass = ['genclass'],", " ijar = ['ijar'])", "java_binary(", " name = 'a',", " srcs = ['a.java'])"); useConfiguration("--java_toolchain=//java/a:toolchain", "--check_licenses"); ConfiguredTarget bin = getConfiguredTarget("//java/a:a"); Map<Label, License> licenses = getTransitiveLicenses(bin); License toolchainLicense = licenses.get(Label.parseAbsoluteUnchecked("//java/a:toolchain")); assertThat(toolchainLicense).isNotEqualTo(License.parseLicense(ImmutableList.of("restricted"))); assertThat(toolchainLicense).isEqualTo(License.parseLicense(ImmutableList.of("unencumbered"))); } @Test public void testTargetLicenseEquality() throws Exception { Label label1 = Label.parseAbsolute("//foo"); Label label2 = Label.parseAbsolute("//bar"); License restricted = License.parseLicense(ImmutableList.of("restricted")); License unencumbered = License.parseLicense(ImmutableList.of("unencumbered")); new EqualsTester() .addEqualityGroup( new TargetLicense(label1, restricted), new TargetLicense(label1, restricted)) .addEqualityGroup( new TargetLicense(label2, restricted), new TargetLicense(label2, restricted)) .addEqualityGroup( new TargetLicense(label2, unencumbered), new TargetLicense(label2, unencumbered)) .testEquals(); } /** * Gets the licenses of all targets that are in the transitive closure of a * target. The result includes this target itself. * * @return a map from target labels to licenses */ protected static Map<Label, License> getTransitiveLicenses(ConfiguredTarget target) { final ImmutableMap.Builder<Label, License> result = ImmutableMap.builder(); LicensesProvider provider = target.getProvider(LicensesProvider.class); if (provider != null) { for (TargetLicense targetLicense : provider.getTransitiveLicenses()) { result.put(targetLicense.getLabel(), targetLicense.getLicense()); } } return result.build(); } }