Accept a single provider instance as return value from an aspect. Previously, if a single provider instance was returned, it was silently dropped. This makes the behavior consistent with rule implementation functions. Fixes #5955 RELNOTES: None. PiperOrigin-RevId: 209777202
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkAspectFactory.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkAspectFactory.java index 12a3e98..150cfb4 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkAspectFactory.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkAspectFactory.java
@@ -29,6 +29,7 @@ import com.google.devtools.build.lib.packages.InfoInterface; import com.google.devtools.build.lib.packages.SkylarkDefinedAspect; import com.google.devtools.build.lib.packages.StructImpl; +import com.google.devtools.build.lib.packages.StructProvider; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.skylarkinterface.SkylarkValue; import com.google.devtools.build.lib.syntax.Environment; @@ -86,10 +87,11 @@ if (ruleContext.hasErrors()) { return null; } else if (!(aspectSkylarkObject instanceof StructImpl) - && !(aspectSkylarkObject instanceof Iterable)) { + && !(aspectSkylarkObject instanceof Iterable) + && !(aspectSkylarkObject instanceof InfoInterface)) { ruleContext.ruleError( - String.format( - "Aspect implementation should return a struct or a list, but got %s", + String.format("Aspect implementation should return a struct, a list, or a provider " + + "instance, but got %s", SkylarkType.typeOf(aspectSkylarkObject))); return null; } @@ -115,25 +117,32 @@ if (aspectSkylarkObject instanceof Iterable) { addDeclaredProviders(builder, (Iterable) aspectSkylarkObject); } else { - StructImpl struct = (StructImpl) aspectSkylarkObject; - Location loc = struct.getCreationLoc(); - for (String field : struct.getFieldNames()) { - if (field.equals("output_groups")) { - addOutputGroups(struct.getValue(field), loc, builder); - } else if (field.equals("providers")) { - Object value = struct.getValue(field); - Iterable providers = - SkylarkType.cast( - value, - Iterable.class, - loc, - "The value for \"providers\" should be a list of declared providers, " - + "got %s instead", - EvalUtils.getDataTypeName(value, false)); - addDeclaredProviders(builder, providers); - } else { - builder.addSkylarkTransitiveInfo(field, struct.getValue(field), loc); + // Either an old-style struct or a single declared provider (not in a list) + InfoInterface info = (InfoInterface) aspectSkylarkObject; + Location loc = info.getCreationLoc(); + if (info.getProvider().getKey().equals(StructProvider.STRUCT.getKey())) { + // Old-style struct, that may contain declared providers. + StructImpl struct = (StructImpl) aspectSkylarkObject; + for (String field : struct.getFieldNames()) { + if (field.equals("output_groups")) { + addOutputGroups(struct.getValue(field), loc, builder); + } else if (field.equals("providers")) { + Object value = struct.getValue(field); + Iterable providers = + SkylarkType.cast( + value, + Iterable.class, + loc, + "The value for \"providers\" should be a list of declared providers, " + + "got %s instead", + EvalUtils.getDataTypeName(value, false)); + addDeclaredProviders(builder, providers); + } else { + builder.addSkylarkTransitiveInfo(field, struct.getValue(field), loc); + } } + } else { + builder.addSkylarkDeclaredProvider(info); } }
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkDefinedAspectsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkDefinedAspectsTest.java index 248d609..c39a1fc 100644 --- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkDefinedAspectsTest.java +++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkDefinedAspectsTest.java
@@ -89,6 +89,30 @@ } @Test + public void aspectWithSingleDeclaredProvider() throws Exception { + scratch.file( + "test/aspect.bzl", + "foo = provider()", + "def _impl(target, ctx):", + " return foo()", + "MyAspect = aspect(implementation=_impl)"); + scratch.file("test/BUILD", "java_library(name = 'xxx',)"); + + AnalysisResult analysisResult = + update(ImmutableList.of("test/aspect.bzl%MyAspect"), "//test:xxx"); + assertThat(getLabelsToBuild(analysisResult)).containsExactly("//test:xxx"); + assertThat(getAspectDescriptions(analysisResult)) + .containsExactly("//test:aspect.bzl%MyAspect(//test:xxx)"); + ConfiguredAspect configuredAspect = Iterables.getOnlyElement(analysisResult.getAspects()) + .getConfiguredAspect(); + + SkylarkKey fooKey = + new SkylarkKey(Label.parseAbsolute("//test:aspect.bzl", ImmutableMap.of()), "foo"); + + assertThat(configuredAspect.get(fooKey).getProvider().getKey()).isEqualTo(fooKey); + } + + @Test public void aspectWithDeclaredProviders() throws Exception { scratch.file( "test/aspect.bzl", @@ -868,7 +892,8 @@ } catch (ViewCreationFailedException e) { // expect to fail. } - assertContainsEvent("Aspect implementation should return a struct or a list, but got int"); + assertContainsEvent("Aspect implementation should return a struct, a list, or a provider " + + "instance, but got int"); } @Test