aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar cparsons <cparsons@google.com>2018-04-27 13:04:59 -0700
committerGravatar Copybara-Service <copybara-piper@google.com>2018-04-27 13:07:03 -0700
commitaab9868f535e3a08e272570fed5fec3c51cd384b (patch)
tree4e1742d8aad4495b9c25b8c80addfd2aae2e1d93
parent758287532e338401146a8bb447161711b4b939c0 (diff)
Allow skylark rule definitions to advertise providers that targets of the rule must propagate
This allows native aspects which specifically require advertised providers to be applied to skylark rules. Implementation to allow aspects to explicitly declare provider requirements will come later. RELNOTES: Skylark rule definitions may advertise providers that targets of the rule must propagate. PiperOrigin-RevId: 194581466
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java2
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java36
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleConfiguredTargetUtil.java23
-rw-r--r--src/test/java/com/google/devtools/build/lib/rules/objc/BazelJ2ObjcLibraryTest.java53
-rw-r--r--src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java2
-rw-r--r--src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java117
6 files changed, 227 insertions, 6 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
index 987177e8b3..62fd1a7a0f 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
@@ -366,7 +366,9 @@ public final class ConfiguredTargetFactory {
// TODO(bazel-team): maybe merge with RuleConfiguredTargetBuilder?
return SkylarkRuleConfiguredTargetUtil.buildRule(
ruleContext,
+ rule.getRuleClassObject().getAdvertisedProviders(),
rule.getRuleClassObject().getConfiguredTargetFunction(),
+ rule.getLocation(),
env.getSkylarkSemantics());
} else {
RuleClass.ConfiguredTargetFactory<ConfiguredTarget, RuleContext, ActionConflictException>
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
index 5f7c2bf8db..18db4d3849 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
@@ -68,6 +68,7 @@ import com.google.devtools.build.lib.packages.SkylarkAspect;
import com.google.devtools.build.lib.packages.SkylarkDefinedAspect;
import com.google.devtools.build.lib.packages.SkylarkExportable;
import com.google.devtools.build.lib.packages.SkylarkProvider;
+import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
import com.google.devtools.build.lib.packages.TargetUtils;
import com.google.devtools.build.lib.packages.TestSize;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
@@ -532,7 +533,18 @@ public class SkylarkRuleClassFunctions {
legacyNamed = true,
defaultValue = "''",
doc = "A description of the rule that can be extracted by documentation generating tools."
- )
+ ),
+ @Param(
+ name = "provides",
+ type = SkylarkList.class,
+ named = true,
+ positional = false,
+ defaultValue = "[]",
+ doc =
+ "A list of providers this rule is guaranteed to provide. "
+ + "It is an error if a provider is listed here and the rule "
+ + "implementation function does not return it."
+ ),
},
useAst = true,
useEnvironment = true
@@ -546,11 +558,12 @@ public class SkylarkRuleClassFunctions {
Object implicitOutputs,
Boolean executable,
Boolean outputToGenfiles,
- SkylarkList fragments,
- SkylarkList hostFragments,
+ SkylarkList<?> fragments,
+ SkylarkList<?> hostFragments,
Boolean skylarkTestable,
SkylarkList<String> toolchains,
String doc,
+ SkylarkList<?> providesArg,
FuncallExpression ast,
Environment funcallEnv)
throws EvalException, ConversionException {
@@ -617,6 +630,21 @@ public class SkylarkRuleClassFunctions {
funcallEnv.getTransitiveContentHashCode());
builder.addRequiredToolchains(collectToolchainLabels(toolchains, ast));
+ for (Object o : providesArg) {
+ if (!SkylarkAttr.isProvider(o)) {
+ throw new EvalException(
+ ast.getLocation(),
+ String.format(
+ "Illegal argument: element in 'provides' is of unexpected type. "
+ + "Should be list of providers, but got item of type %s.",
+ EvalUtils.getDataTypeName(o, true)));
+ }
+ }
+ for (SkylarkProviderIdentifier skylarkProvider :
+ SkylarkAttr.getSkylarkProviderIdentifiers(providesArg, ast.getLocation())) {
+ builder.advertiseSkylarkProvider(skylarkProvider);
+ }
+
return new SkylarkRuleFunction(builder, type, attributes, ast.getLocation());
}
@@ -863,7 +891,7 @@ public class SkylarkRuleClassFunctions {
ast.getLocation(),
String.format(
"Illegal argument: element in 'provides' is of unexpected type. "
- + "Should be list of providers, but got %s. ",
+ + "Should be list of providers, but got item of type %s. ",
EvalUtils.getDataTypeName(o, true)));
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleConfiguredTargetUtil.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleConfiguredTargetUtil.java
index f1aac64627..1cc7543472 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleConfiguredTargetUtil.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleConfiguredTargetUtil.java
@@ -33,10 +33,12 @@ import com.google.devtools.build.lib.analysis.test.InstrumentedFilesProvider;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.AdvertisedProviderSet;
import com.google.devtools.build.lib.packages.Info;
import com.google.devtools.build.lib.packages.NativeProvider;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
+import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
import com.google.devtools.build.lib.packages.TargetUtils;
import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
import com.google.devtools.build.lib.syntax.BaseFunction;
@@ -73,7 +75,11 @@ public final class SkylarkRuleConfiguredTargetUtil {
/** Create a Rule Configured Target from the ruleContext and the ruleImplementation. */
public static ConfiguredTarget buildRule(
- RuleContext ruleContext, BaseFunction ruleImplementation, SkylarkSemantics skylarkSemantics)
+ RuleContext ruleContext,
+ AdvertisedProviderSet advertisedProviders,
+ BaseFunction ruleImplementation,
+ Location location,
+ SkylarkSemantics skylarkSemantics)
throws InterruptedException, RuleErrorException, ActionConflictException {
String expectFailure = ruleContext.attributes().get("expect_failure", Type.STRING);
SkylarkRuleContext skylarkRuleContext = null;
@@ -108,6 +114,7 @@ public final class SkylarkRuleConfiguredTargetUtil {
}
ConfiguredTarget configuredTarget = createTarget(skylarkRuleContext, target);
SkylarkProviderValidationUtil.checkOrphanArtifacts(ruleContext);
+ checkDeclaredProviders(configuredTarget, advertisedProviders, location);
return configuredTarget;
} catch (EvalException e) {
addRuleToStackTrace(e, ruleContext.getRule(), ruleImplementation);
@@ -126,6 +133,20 @@ public final class SkylarkRuleConfiguredTargetUtil {
}
}
+ private static void checkDeclaredProviders(
+ ConfiguredTarget configuredTarget, AdvertisedProviderSet advertisedProviders, Location loc)
+ throws EvalException {
+ for (SkylarkProviderIdentifier providerId : advertisedProviders.getSkylarkProviders()) {
+ if (configuredTarget.get(providerId) == null) {
+ throw new EvalException(
+ loc,
+ String.format(
+ "rule advertised the '%s' provider, but this provider was not among those returned",
+ providerId.toString()));
+ }
+ }
+ }
+
/**
* Adds the given rule to the stack trace of the exception (if there is one).
*/
diff --git a/src/test/java/com/google/devtools/build/lib/rules/objc/BazelJ2ObjcLibraryTest.java b/src/test/java/com/google/devtools/build/lib/rules/objc/BazelJ2ObjcLibraryTest.java
index 29db32ad0c..88d1babc68 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/objc/BazelJ2ObjcLibraryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/objc/BazelJ2ObjcLibraryTest.java
@@ -884,6 +884,59 @@ public class BazelJ2ObjcLibraryTest extends J2ObjcLibraryTest {
"java/com/google/dummy/_j2objc/dummy/java/com/google/dummy/dummy.m"));
}
+ // Tests that a j2objc library can acquire java library information from a skylark rule target.
+ @Test
+ public void testJ2ObjcLibraryDepThroughSkylarkRule() throws Exception {
+ scratch.file("examples/inner.java");
+ scratch.file("examples/outer.java");
+ scratch.file(
+ "examples/fake_rule.bzl",
+ "def _fake_rule_impl(ctx):",
+ " myProvider = ctx.attr.deps[0][JavaInfo]",
+ " return struct(providers = [myProvider])",
+ "",
+ "fake_rule = rule(",
+ " implementation = _fake_rule_impl,",
+ " attrs = {'deps': attr.label_list()},",
+ " provides = [JavaInfo],",
+ ")");
+ scratch.file(
+ "examples/BUILD",
+ "package(default_visibility=['//visibility:public'])",
+ "load('//examples:fake_rule.bzl', 'fake_rule')",
+ "java_library(",
+ " name = 'inner',",
+ " srcs = ['inner.java'])",
+ "",
+ "fake_rule(",
+ " name = 'propagator',",
+ " deps = [':inner'])",
+ "",
+ "java_library(",
+ " name = 'outer',",
+ " srcs = ['outer.java'],",
+ " deps = [':propagator'])",
+ "",
+ "j2objc_library(",
+ " name = 'transpile',",
+ " deps = [",
+ " ':outer',",
+ " ])",
+ "",
+ "objc_library(",
+ " name = 'lib',",
+ " srcs = ['lib.m'],",
+ " deps = [':transpile'])");
+
+ ConfiguredTarget objcTarget = getConfiguredTarget("//examples:lib");
+
+ ObjcProvider provider = objcTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR);
+
+ // The only way that //examples:lib can see inner's archive is through the skylark rule.
+ assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.LIBRARY)))
+ .contains("examples/libinner_j2objc.a");
+ }
+
@Test
public void testJ2ObjcTranspiledHeaderInCompilationAction() throws Exception {
scratch.file("app/lib.m");
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
index 10df59c01d..7bb4b39ace 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
@@ -1450,7 +1450,7 @@ public class SkylarkRuleClassFunctionsTest extends SkylarkTestCase {
);
MoreAsserts.assertContainsEvent(ev.getEventCollector(),
" Illegal argument: element in 'provides' is of unexpected type."
- + " Should be list of providers, but got int. ");
+ + " Should be list of providers, but got item of type int. ");
}
@Test
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 71b1ca9c92..dc8b9209c0 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
@@ -1314,6 +1314,123 @@ public class SkylarkRuleImplementationFunctionsTest extends SkylarkTestCase {
}
@Test
+ public void testAdvertisedProviders() throws Exception {
+ scratch.file(
+ "test/foo.bzl",
+ "FooInfo = provider()",
+ "BarInfo = provider()",
+ "def _impl(ctx):",
+ " foo = FooInfo()",
+ " bar = BarInfo()",
+ " return [foo, bar]",
+ "foo_rule = rule(",
+ " implementation = _impl,",
+ " provides = [FooInfo, BarInfo]",
+ ")");
+ scratch.file(
+ "test/bar.bzl",
+ "load(':foo.bzl', 'FooInfo')",
+ "def _impl(ctx):",
+ " dep = ctx.attr.deps[0]",
+ " proxy = dep[FooInfo]", // The goal is to test this object
+ " return struct(proxy = proxy)", // so we return it here
+ "bar_rule = rule(",
+ " implementation = _impl,",
+ " attrs = {",
+ " '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.get("proxy");
+ assertThat(provider).isInstanceOf(Info.class);
+ assertThat(((Info) provider).getProvider().getKey())
+ .isEqualTo(new SkylarkKey(Label.parseAbsolute("//test:foo.bzl"), "FooInfo"));
+ }
+
+ @Test
+ public void testLacksAdvertisedDeclaredProvider() throws Exception {
+ scratch.file(
+ "test/foo.bzl",
+ "FooInfo = provider()",
+ "def _impl(ctx):",
+ " default = DefaultInfo(",
+ " runfiles=ctx.runfiles(ctx.files.runs),",
+ " )",
+ " return struct(providers=[default])",
+ "foo_rule = rule(",
+ " implementation = _impl,",
+ " attrs = {",
+ " 'runs': attr.label_list(allow_files=True),",
+ " },",
+ " provides = [FooInfo, DefaultInfo]",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load(':foo.bzl', 'foo_rule')",
+ "foo_rule(name = 'my_rule', runs = ['run.file', 'run2.file'])");
+
+ AssertionError expected =
+ assertThrows(AssertionError.class, () -> getConfiguredTarget("//test:my_rule"));
+ assertThat(expected)
+ .hasMessageThat()
+ .contains("rule advertised the 'FooInfo' provider, "
+ + "but this provider was not among those returned");
+ }
+
+ @Test
+ public void testLacksAdvertisedNativeProvider() throws Exception {
+ scratch.file(
+ "test/foo.bzl",
+ "FooInfo = provider()",
+ "def _impl(ctx):",
+ " MyFooInfo = FooInfo()",
+ " return struct(providers=[MyFooInfo])",
+ "foo_rule = rule(",
+ " implementation = _impl,",
+ " provides = [FooInfo, JavaInfo]",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load(':foo.bzl', 'foo_rule')",
+ "foo_rule(name = 'my_rule')");
+
+ AssertionError expected =
+ assertThrows(AssertionError.class, () -> getConfiguredTarget("//test:my_rule"));
+ assertThat(expected)
+ .hasMessageThat()
+ .contains("rule advertised the 'JavaInfo' provider, "
+ + "but this provider was not among those returned");
+ }
+
+ @Test
+ public void testBadlySpecifiedProvides() throws Exception {
+ scratch.file(
+ "test/foo.bzl",
+ "def _impl(ctx):",
+ " return struct()",
+ "foo_rule = rule(",
+ " implementation = _impl,",
+ " provides = [1]",
+ ")");
+ scratch.file("test/BUILD", "load(':foo.bzl', 'foo_rule')", "foo_rule(name = 'my_rule')");
+
+
+ AssertionError expected =
+ assertThrows(AssertionError.class, () -> getConfiguredTarget("//test:my_rule"));
+ assertThat(expected)
+ .hasMessageThat()
+ .contains(
+ "element in 'provides' is of unexpected type. "
+ + "Should be list of providers, but got item of type int");
+ }
+
+ @Test
public void testSingleDeclaredProvider() throws Exception {
scratch.file(
"test/foo.bzl",