Implemented declared providers interface #1727
--
MOS_MIGRATED_REVID=133699895
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AbstractConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/AbstractConfiguredTarget.java
index ca5aac4..718c67b 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/AbstractConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/AbstractConfiguredTarget.java
@@ -22,9 +22,13 @@
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;
+import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.packages.PackageSpecification;
+import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.syntax.ClassObject;
+import com.google.devtools.build.lib.syntax.EvalException;
+import com.google.devtools.build.lib.syntax.EvalUtils;
import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
import javax.annotation.Nullable;
@@ -115,6 +119,27 @@
}
@Override
+ public Object getIndex(Object key, Location loc) throws EvalException {
+ if (!(key instanceof SkylarkClassObjectConstructor)) {
+ throw new EvalException(loc, String.format(
+ "Type Target only supports indexing by object constructors, got %s instead",
+ EvalUtils.getDataTypeName(key)));
+ }
+ SkylarkClassObjectConstructor constructor = (SkylarkClassObjectConstructor) key;
+ SkylarkProviders provider = getProvider(SkylarkProviders.class);
+ if (provider != null) {
+ Object declaredProvider = provider.getDeclaredProvider(constructor.getKey());
+ if (declaredProvider != null) {
+ return declaredProvider;
+ }
+ }
+ // Either provider or declaredProvider is null
+ throw new EvalException(loc, String.format(
+ "Object of type Target doesn't contain declared provider %s",
+ constructor.getKey().getExportedName()));
+ }
+
+ @Override
public String errorMessage(String name) {
return null;
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java b/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java
index 6dd8dd7..d1a9ece 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java
@@ -18,7 +18,7 @@
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
-
+import com.google.devtools.build.lib.syntax.SkylarkIndexable;
import javax.annotation.Nullable;
/**
@@ -52,7 +52,7 @@
+ "These extra providers are defined in the <code>struct</code> returned by the rule "
+ "implementation function.</li>"
+ "</ul>")
-public interface TransitiveInfoCollection {
+public interface TransitiveInfoCollection extends SkylarkIndexable {
/**
* Returns the transitive information provider requested, or null if the provider is not found.
diff --git a/src/main/java/com/google/devtools/build/lib/rules/AliasConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/rules/AliasConfiguredTarget.java
index 596675f..ec184b4 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/AliasConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/AliasConfiguredTarget.java
@@ -26,11 +26,12 @@
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.syntax.ClassObject;
+import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
import com.google.devtools.build.lib.util.Preconditions;
-
import javax.annotation.Nullable;
/**
@@ -75,6 +76,11 @@
}
@Override
+ public Object getIndex(Object key, Location loc) throws EvalException {
+ return actual == null ? null : actual.getIndex(key, loc);
+ }
+
+ @Override
public Target getTarget() {
return actual == null ? null : actual.getTarget();
}
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java
index f56f15b..60b9643 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java
@@ -28,11 +28,13 @@
import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.Runfiles;
+import com.google.devtools.build.lib.analysis.SkylarkProviders;
import com.google.devtools.build.lib.analysis.actions.FileWriteAction;
import com.google.devtools.build.lib.analysis.actions.SpawnAction;
import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction;
import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.Substitution;
import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.packages.SkylarkClassObject;
import com.google.devtools.build.lib.rules.SkylarkRuleContext;
import com.google.devtools.build.lib.skylark.util.SkylarkTestCase;
import com.google.devtools.build.lib.skylarkinterface.Param;
@@ -46,8 +48,12 @@
import com.google.devtools.build.lib.syntax.SkylarkList.MutableList;
import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
import com.google.devtools.build.lib.testutil.MoreAsserts;
-
import com.google.devtools.build.lib.util.OsUtils;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -55,12 +61,6 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Pattern;
-
/**
* Tests for SkylarkRuleImplementationFunctions.
*/
@@ -831,6 +831,216 @@
}
@Test
+ public void testDeclaredProviders() throws Exception {
+ scratch.file(
+ "test/foo.bzl",
+ "foo_provider = provider()",
+ "foobar_provider = provider()",
+ "def _impl(ctx):",
+ " foo = foo_provider()",
+ " foobar = foobar_provider()",
+ " return [foo, foobar]",
+ "foo_rule = rule(",
+ " implementation = _impl,",
+ " attrs = {",
+ " \"srcs\": attr.label_list(allow_files=True),",
+ " }",
+ ")"
+ );
+ scratch.file(
+ "test/bar.bzl",
+ "load(':foo.bzl', 'foo_provider')",
+ "def _impl(ctx):",
+ " dep = ctx.attr.deps[0]",
+ " provider = dep[foo_provider]", // The goal is to test this object
+ " return struct(proxy = provider)", // so we return it here
+ "bar_rule = rule(",
+ " implementation = _impl,",
+ " attrs = {",
+ " 'srcs': attr.label_list(allow_files=True),",
+ " 'deps': attr.label_list(allow_files=True),",
+ " }",
+ ")"
+ );
+ scratch.file(
+ "test/BUILD",
+ "load(':foo.bzl', 'foo_rule')",
+ "load(':bar.bzl', 'bar_rule')",
+ "foo_rule(name = 'dep_rule')",
+ "bar_rule(name = 'my_rule', deps = [':dep_rule'])");
+ ConfiguredTarget configuredTarget = getConfiguredTarget("//test:my_rule");
+ Object provider = configuredTarget.getProvider(SkylarkProviders.class).getValue("proxy");
+ assertThat(provider).isInstanceOf(SkylarkClassObject.class);
+ assertThat(((SkylarkClassObject) provider).getConstructor().getKey().getExportedName())
+ .isEqualTo("foo_provider");
+ }
+
+ @Test
+ public void testDeclaredProvidersAliasTarget() throws Exception {
+ scratch.file(
+ "test/foo.bzl",
+ "foo_provider = provider()",
+ "foobar_provider = provider()",
+ "def _impl(ctx):",
+ " foo = foo_provider()",
+ " foobar = foobar_provider()",
+ " return [foo, foobar]",
+ "foo_rule = rule(",
+ " implementation = _impl,",
+ " attrs = {",
+ " \"srcs\": attr.label_list(allow_files=True),",
+ " }",
+ ")"
+ );
+ scratch.file(
+ "test/bar.bzl",
+ "load(':foo.bzl', 'foo_provider')",
+ "def _impl(ctx):",
+ " dep = ctx.attr.deps[0]",
+ " provider = dep[foo_provider]", // The goal is to test this object
+ " return struct(proxy = provider)", // so we return it here
+ "bar_rule = rule(",
+ " implementation = _impl,",
+ " attrs = {",
+ " 'srcs': attr.label_list(allow_files=True),",
+ " 'deps': attr.label_list(allow_files=True),",
+ " }",
+ ")"
+ );
+ scratch.file(
+ "test/BUILD",
+ "load(':foo.bzl', 'foo_rule')",
+ "load(':bar.bzl', 'bar_rule')",
+ "foo_rule(name = 'foo_rule')",
+ "alias(name = 'dep_rule', actual=':foo_rule')",
+ "bar_rule(name = 'my_rule', deps = [':dep_rule'])");
+ ConfiguredTarget configuredTarget = getConfiguredTarget("//test:my_rule");
+ Object provider = configuredTarget.getProvider(SkylarkProviders.class).getValue("proxy");
+ assertThat(provider).isInstanceOf(SkylarkClassObject.class);
+ assertThat(((SkylarkClassObject) provider).getConstructor().getKey().getExportedName())
+ .isEqualTo("foo_provider");
+ }
+
+ @Test
+ public void testDeclaredProvidersWrongKey() throws Exception {
+ scratch.file(
+ "test/foo.bzl",
+ "foo_provider = provider()",
+ "unused_provider = provider()",
+ "def _impl(ctx):",
+ " foo = foo_provider()",
+ " return [foo]",
+ "foo_rule = rule(",
+ " implementation = _impl,",
+ " attrs = {",
+ " \"srcs\": attr.label_list(allow_files=True),",
+ " }",
+ ")"
+ );
+ scratch.file(
+ "test/bar.bzl",
+ "load(':foo.bzl', 'unused_provider')",
+ "def _impl(ctx):",
+ " dep = ctx.attr.deps[0]",
+ " provider = dep[unused_provider]", // Should throw an error here
+ "bar_rule = rule(",
+ " implementation = _impl,",
+ " attrs = {",
+ " 'srcs': attr.label_list(allow_files=True),",
+ " 'deps': attr.label_list(allow_files=True),",
+ " }",
+ ")"
+ );
+ scratch.file(
+ "test/BUILD",
+ "load(':foo.bzl', 'foo_rule')",
+ "load(':bar.bzl', 'bar_rule')",
+ "foo_rule(name = 'dep_rule')",
+ "bar_rule(name = 'my_rule', deps = [':dep_rule'])");
+
+ try {
+ getConfiguredTarget("//test:my_rule");
+ fail();
+ } catch (AssertionError expected) {
+ assertThat(expected.getMessage()).contains("Object of type Target doesn't "
+ + "contain declared provider unused_provider");
+ }
+ }
+
+ @Test
+ public void testDeclaredProvidersInvalidKey() throws Exception {
+ scratch.file(
+ "test/foo.bzl",
+ "foo_provider = provider()",
+ "def _impl(ctx):",
+ " foo = foo_provider()",
+ " return [foo]",
+ "foo_rule = rule(",
+ " implementation = _impl,",
+ " attrs = {",
+ " \"srcs\": attr.label_list(allow_files=True),",
+ " }",
+ ")"
+ );
+ scratch.file(
+ "test/bar.bzl",
+ "def _impl(ctx):",
+ " dep = ctx.attr.deps[0]",
+ " provider = dep['foo_provider']", // Should throw an error here
+ "bar_rule = rule(",
+ " implementation = _impl,",
+ " attrs = {",
+ " 'srcs': attr.label_list(allow_files=True),",
+ " 'deps': attr.label_list(allow_files=True),",
+ " }",
+ ")"
+ );
+ scratch.file(
+ "test/BUILD",
+ "load(':foo.bzl', 'foo_rule')",
+ "load(':bar.bzl', 'bar_rule')",
+ "foo_rule(name = 'dep_rule')",
+ "bar_rule(name = 'my_rule', deps = [':dep_rule'])");
+
+ try {
+ getConfiguredTarget("//test:my_rule");
+ fail();
+ } catch (AssertionError expected) {
+ assertThat(expected.getMessage()).contains("Type Target only supports indexing "
+ + "by object constructors, got string instead");
+ }
+ }
+
+ @Test
+ public void testDeclaredProvidersFileTarget() throws Exception {
+ scratch.file(
+ "test/bar.bzl",
+ "unused_provider = provider()",
+ "def _impl(ctx):",
+ " src = ctx.attr.srcs[0]",
+ " provider = src[unused_provider]", // Should throw an error here
+ "bar_rule = rule(",
+ " implementation = _impl,",
+ " attrs = {",
+ " 'srcs': attr.label_list(allow_files=True),",
+ " }",
+ ")"
+ );
+ scratch.file(
+ "test/BUILD",
+ "load(':bar.bzl', 'bar_rule')",
+ "bar_rule(name = 'my_rule', srcs = ['input.txt'])");
+
+ try {
+ getConfiguredTarget("//test:my_rule");
+ fail();
+ } catch (AssertionError expected) {
+ assertThat(expected.getMessage()).contains("Object of type Target doesn't "
+ + "contain declared provider unused_provider");
+ }
+ }
+
+ @Test
public void testFilesForFileConfiguredTarget() throws Exception {
Object result =
evalRuleContextCode(createRuleContext("//foo:bar"), "ruleContext.attr.srcs[0].files");