diff --git a/src/main/java/com/google/devtools/build/lib/analysis/PackageSpecificationProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/PackageSpecificationProvider.java index a6ce95562ae2a0..72b778ad2803f3 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/PackageSpecificationProvider.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/PackageSpecificationProvider.java @@ -15,6 +15,7 @@ package com.google.devtools.build.lib.analysis; import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.collect.nestedset.Order; @@ -26,6 +27,8 @@ import com.google.devtools.build.lib.packages.Provider; import com.google.devtools.build.lib.starlarkbuildapi.PackageSpecificationProviderApi; import java.util.Optional; +import net.starlark.java.eval.EvalException; +import net.starlark.java.eval.Starlark; /** * A {@link TransitiveInfoProvider} that describes a set of transitive package specifications used @@ -92,4 +95,19 @@ private static NestedSet getPackageSpecifications( builder.add(packageGroup.getPackageSpecifications()); return builder.build(); } + + @Override + public boolean targetInAllowlist(Object target) throws EvalException, LabelSyntaxException { + Label targetLabel; + if (target instanceof String) { + targetLabel = Label.parseCanonical((String) target); + } else if (target instanceof Label) { + targetLabel = (Label) target; + } else { + throw Starlark.errorf( + "expected string or label for 'target' instead of %s", Starlark.type(target)); + } + + return Allowlist.isAvailableFor(packageSpecifications, targetLabel); + } } diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/PackageSpecificationProviderApi.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/PackageSpecificationProviderApi.java index c313719dcbbd5c..c81eb49665d217 100644 --- a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/PackageSpecificationProviderApi.java +++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/PackageSpecificationProviderApi.java @@ -14,12 +14,30 @@ package com.google.devtools.build.lib.starlarkbuildapi; import com.google.devtools.build.docgen.annot.DocCategory; +import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.starlarkbuildapi.core.StructApi; +import net.starlark.java.annot.Param; +import net.starlark.java.annot.ParamType; import net.starlark.java.annot.StarlarkBuiltin; +import net.starlark.java.annot.StarlarkMethod; +import net.starlark.java.eval.EvalException; /** Provider which describes a set of transitive package specifications used in package groups. */ @StarlarkBuiltin( name = "PackageSpecificationInfo", doc = "Information about transitive package specifications used in package groups.", category = DocCategory.PROVIDER) -public interface PackageSpecificationProviderApi extends StructApi {} +public interface PackageSpecificationProviderApi extends StructApi { + @StarlarkMethod( + name = "contains", + doc = "Checks if a target exists in a package group.", + parameters = { + @Param( + name = "target", + positional = true, + doc = "A target which is checked if it exists inside the package group.", + allowedTypes = {@ParamType(type = Label.class), @ParamType(type = String.class)}) + }) + public boolean targetInAllowlist(Object target) throws EvalException, LabelSyntaxException; +} diff --git a/src/test/java/com/google/devtools/build/lib/analysis/allowlisting/AllowlistTest.java b/src/test/java/com/google/devtools/build/lib/analysis/allowlisting/AllowlistTest.java index 828bbe7b343580..d65485406ee7ab 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/allowlisting/AllowlistTest.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/allowlisting/AllowlistTest.java @@ -125,4 +125,156 @@ public void testIncludes() throws Exception { getConfiguredTarget("//x:x"); assertNoEvents(); } + + @Test + public void targetInAllowlist_targetAsStringParameter() throws Exception { + scratch.file( + "allowlist/BUILD", + "package_group(", + " name='allowlist',", + " packages=[", + " '//direct',", + " ])"); + scratch.file( + "test/rule.bzl", + "def _impl(ctx):", + " target = '//direct:rule_from_allowlist'", + " target_in_allowlist =" + + " ctx.attr._allowlist_test[PackageSpecificationInfo].contains(target)", + " if not target_in_allowlist:", + " fail('Target should be in the allowlist')", + " return []", + "custom_rule = rule(", + " implementation = _impl,", + " attrs = {", + " '_allowlist_test': attr.label(", + " default = '//allowlist:allowlist',", + " cfg = 'exec',", + " providers = ['PackageSpecificationInfo']", + " ),", + " },", + ")"); + scratch.file( + "test/BUILD", + "load('//test:rule.bzl', 'custom_rule')", + "custom_rule(name='allowlist_rule')"); + + getConfiguredTarget("//test:allowlist_rule"); + + assertNoEvents(); + } + + @Test + public void targetInAllowlist_targetAsLabelParameter() throws Exception { + scratch.file( + "allowlist/BUILD", + "package_group(", + " name='allowlist',", + " packages=[", + " '//test',", + " ])"); + scratch.file( + "test/rule.bzl", + "def _impl(ctx):", + " target = ctx.label", + " target_in_allowlist =" + + " ctx.attr._allowlist_test[PackageSpecificationInfo].contains(target)", + " if not target_in_allowlist:", + " fail('Target should be in the allowlist')", + " return []", + "custom_rule = rule(", + " implementation = _impl,", + " attrs = {", + " '_allowlist_test': attr.label(", + " default = '//allowlist:allowlist',", + " cfg = 'exec',", + " providers = [PackageSpecificationInfo]", + " ),", + " },", + ")"); + scratch.file( + "test/BUILD", + "load('//test:rule.bzl', 'custom_rule')", + "custom_rule(name='allowlist_rule')"); + + getConfiguredTarget("//test:allowlist_rule"); + + assertNoEvents(); + } + + @Test + public void targetNotInAllowlist() throws Exception { + scratch.file( + "allowlist/BUILD", + "package_group(", + " name='allowlist',", + " packages=[", + " '//direct',", + " ])"); + scratch.file( + "test/rule.bzl", + "def _impl(ctx):", + " target = '//non_direct:rule_not_from_allowlist'", + " target_in_allowlist =" + + " ctx.attr._allowlist_test[PackageSpecificationInfo].contains(target)", + " if target_in_allowlist:", + " fail('Target should not be in the allowlist')", + " return []", + "custom_rule = rule(", + " implementation = _impl,", + " attrs = {", + " '_allowlist_test': attr.label(", + " default = '//allowlist:allowlist',", + " cfg = 'exec',", + " providers = [PackageSpecificationInfo]", + " ),", + " },", + ")"); + scratch.file( + "test/BUILD", + "load('//test:rule.bzl', 'custom_rule')", + "custom_rule(name='allowlist_rule')"); + + getConfiguredTarget("//test:allowlist_rule"); + + assertNoEvents(); + } + + @Test + public void targetNotInAllowlist_negativePath() throws Exception { + scratch.file( + "allowlist/BUILD", + "package_group(", + " name='allowlist',", + " packages=[", + " '-//direct',", + " ])"); + scratch.file( + "test/rule.bzl", + "def _impl(ctx):", + " target = '//direct:rule_from_allowlist'", + " target_in_allowlist =" + + " ctx.attr._allowlist_test[PackageSpecificationInfo].contains(target)", + " if target_in_allowlist:", + " fail('Target should not be in the allowlist (negative path)')", + " return []", + "custom_rule = rule(", + " implementation = _impl,", + " attrs = {", + " '_allowlist_test': attr.label(", + " default = '//allowlist:allowlist',", + " cfg = 'exec',", + " providers = [PackageSpecificationInfo]", + " ),", + " },", + ")"); + scratch.file( + "test/BUILD", + "load('//test:rule.bzl', 'custom_rule')", + "custom_rule(name='allowlist_rule')"); + + getConfiguredTarget("//test:allowlist_rule"); + + assertNoEvents(); + } }