diff options
author | Vladimir Moskva <vladmos@google.com> | 2016-11-09 12:54:51 +0000 |
---|---|---|
committer | Klaus Aehlig <aehlig@google.com> | 2016-11-09 13:45:00 +0000 |
commit | eb0cfc57b58d646ad6a67c655fe0a76b4c22bdbd (patch) | |
tree | 2a79fbb7e02ae988431eb36d1aad6627c3158001 /src/main/java/com/google/devtools | |
parent | 90b5b19927540fc1e1c9e1a7fbd05c816ae0e4e2 (diff) |
Implemented default provider
--
MOS_MIGRATED_REVID=138625702
Diffstat (limited to 'src/main/java/com/google/devtools')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleConfiguredTargetBuilder.java | 214 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java | 8 |
2 files changed, 142 insertions, 80 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleConfiguredTargetBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleConfiguredTargetBuilder.java index 814ae564b8..94c507787d 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleConfiguredTargetBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleConfiguredTargetBuilder.java @@ -13,6 +13,7 @@ // limitations under the License. package com.google.devtools.build.lib.rules; +import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.devtools.build.lib.actions.Artifact; @@ -230,93 +231,141 @@ public final class SkylarkRuleConfiguredTargetBuilder { Map<String, Class<? extends TransitiveInfoProvider>> registeredProviderTypes) throws EvalException { Location loc = null; - Runfiles statelessRunfiles = null; - Runfiles dataRunfiles = null; - Runfiles defaultRunfiles = null; + Boolean isParsed = false; if (target instanceof SkylarkClassObject) { SkylarkClassObject struct = (SkylarkClassObject) target; loc = struct.getCreationLoc(); - for (String key : struct.getKeys()) { - if (key.equals("files")) { - // If we specify files_to_build we don't have the executable in it by default. - builder.setFilesToBuild(cast("files", struct, SkylarkNestedSet.class, Artifact.class, loc) - .getSet(Artifact.class)); - } else if (key.equals("runfiles")) { - statelessRunfiles = cast("runfiles", struct, Runfiles.class, loc); - } else if (key.equals("data_runfiles")) { - dataRunfiles = cast("data_runfiles", struct, Runfiles.class, loc); - } else if (key.equals("default_runfiles")) { - defaultRunfiles = cast("default_runfiles", struct, Runfiles.class, loc); - } else if (key.equals("output_groups")) { - addOutputGroups(struct.getValue(key), loc, builder); - } else if (key.equals("instrumented_files")) { - SkylarkClassObject insStruct = - cast("instrumented_files", struct, SkylarkClassObject.class, loc); - Location insLoc = insStruct.getCreationLoc(); - FileTypeSet fileTypeSet = FileTypeSet.ANY_FILE; - if (insStruct.getKeys().contains("extensions")) { - @SuppressWarnings("unchecked") - List<String> exts = cast( - "extensions", insStruct, SkylarkList.class, String.class, insLoc); - if (exts.isEmpty()) { - fileTypeSet = FileTypeSet.NO_FILE; - } else { - FileType[] fileTypes = new FileType[exts.size()]; - for (int i = 0; i < fileTypes.length; i++) { - fileTypes[i] = FileType.of(exts.get(i)); - } - fileTypeSet = FileTypeSet.of(fileTypes); - } - } - List<String> dependencyAttributes = Collections.emptyList(); - if (insStruct.getKeys().contains("dependency_attributes")) { - dependencyAttributes = - cast("dependency_attributes", insStruct, SkylarkList.class, String.class, insLoc); - } - List<String> sourceAttributes = Collections.emptyList(); - if (insStruct.getKeys().contains("source_attributes")) { - sourceAttributes = - cast("source_attributes", insStruct, SkylarkList.class, String.class, insLoc); - } - InstrumentationSpec instrumentationSpec = - new InstrumentationSpec(fileTypeSet) - .withSourceAttributes(sourceAttributes.toArray(new String[0])) - .withDependencyAttributes(dependencyAttributes.toArray(new String[0])); - InstrumentedFilesProvider instrumentedFilesProvider = - InstrumentedFilesCollector.collect( - ruleContext, - instrumentationSpec, - InstrumentedFilesCollector.NO_METADATA_COLLECTOR, - Collections.<Artifact>emptySet()); - builder.addProvider(InstrumentedFilesProvider.class, instrumentedFilesProvider); - } else if (registeredProviderTypes.containsKey(key)) { - Class<? extends TransitiveInfoProvider> providerType = registeredProviderTypes.get(key); - TransitiveInfoProvider provider = cast(key, struct, providerType, loc); - builder.addProvider(providerType, provider); - } else if (key.equals("providers")) { - Iterable iterable = cast(key, struct, Iterable.class, loc); - for (Object o : iterable) { - SkylarkClassObject declaredProvider = SkylarkType.cast(o, SkylarkClassObject.class, loc, - "The value of 'providers' should be a sequence of declared providers"); - builder.addSkylarkDeclaredProvider(declaredProvider, loc); - } - } else if (!key.equals("executable")) { - // We handled executable already. - builder.addSkylarkTransitiveInfo(key, struct.getValue(key), loc); - } - } + parseProviderKeys(struct, false, ruleContext, loc, executable, registeredProviderTypes, + builder); + isParsed = true; } else if (target instanceof Iterable) { loc = ruleContext.getRule().getRuleClassObject().getConfiguredTargetFunction().getLocation(); for (Object o : (Iterable) target) { SkylarkClassObject declaredProvider = SkylarkType.cast(o, SkylarkClassObject.class, loc, "A return value of rule implementation function should be " + "a sequence of declared providers"); - Location creationLoc = declaredProvider.getCreationLocOrNull(); - builder.addSkylarkDeclaredProvider(declaredProvider, - creationLoc != null ? creationLoc : loc); + if (declaredProvider.getConstructor().getKey().equals( + SkylarkRuleContext.getDefaultProvider().getKey())) { + parseProviderKeys(declaredProvider, true, ruleContext, loc, executable, + registeredProviderTypes, builder); + isParsed = true; + } else { + Location creationLoc = declaredProvider.getCreationLocOrNull(); + builder.addSkylarkDeclaredProvider(declaredProvider, + creationLoc != null ? creationLoc : loc); + } } } + if (!isParsed) { + addSimpleProviders(builder, ruleContext, loc, executable, null, null, null, null); + } + + try { + return builder.build(); + } catch (IllegalArgumentException e) { + throw new EvalException(loc, e.getMessage()); + } + } + + private static void parseProviderKeys( + SkylarkClassObject provider, + Boolean isDefaultProvider, + RuleContext ruleContext, + Location loc, + Artifact executable, + Map<String, Class<? extends TransitiveInfoProvider>> registeredProviderTypes, + RuleConfiguredTargetBuilder builder) throws EvalException { + Runfiles statelessRunfiles = null; + Runfiles dataRunfiles = null; + Runfiles defaultRunfiles = null; + + for (String key : provider.getKeys()) { + if (key.equals("files")) { + // If we specify files_to_build we don't have the executable in it by default. + builder.setFilesToBuild(cast("files", provider, SkylarkNestedSet.class, Artifact.class, loc) + .getSet(Artifact.class)); + } else if (key.equals("runfiles")) { + statelessRunfiles = cast("runfiles", provider, Runfiles.class, loc); + } else if (key.equals("data_runfiles")) { + dataRunfiles = cast("data_runfiles", provider, Runfiles.class, loc); + } else if (key.equals("default_runfiles")) { + defaultRunfiles = cast("default_runfiles", provider, Runfiles.class, loc); + } else if (key.equals("output_groups")) { + addOutputGroups(provider.getValue(key), loc, builder); + } else if (key.equals("instrumented_files")) { + SkylarkClassObject insStruct = + cast("instrumented_files", provider, SkylarkClassObject.class, loc); + Location insLoc = insStruct.getCreationLoc(); + FileTypeSet fileTypeSet = FileTypeSet.ANY_FILE; + if (insStruct.getKeys().contains("extensions")) { + @SuppressWarnings("unchecked") + List<String> exts = cast( + "extensions", insStruct, SkylarkList.class, String.class, insLoc); + if (exts.isEmpty()) { + fileTypeSet = FileTypeSet.NO_FILE; + } else { + FileType[] fileTypes = new FileType[exts.size()]; + for (int i = 0; i < fileTypes.length; i++) { + fileTypes[i] = FileType.of(exts.get(i)); + } + fileTypeSet = FileTypeSet.of(fileTypes); + } + } + List<String> dependencyAttributes = Collections.emptyList(); + if (insStruct.getKeys().contains("dependency_attributes")) { + dependencyAttributes = + cast("dependency_attributes", insStruct, SkylarkList.class, String.class, insLoc); + } + List<String> sourceAttributes = Collections.emptyList(); + if (insStruct.getKeys().contains("source_attributes")) { + sourceAttributes = + cast("source_attributes", insStruct, SkylarkList.class, String.class, insLoc); + } + InstrumentationSpec instrumentationSpec = + new InstrumentationSpec(fileTypeSet) + .withSourceAttributes(sourceAttributes.toArray(new String[0])) + .withDependencyAttributes(dependencyAttributes.toArray(new String[0])); + InstrumentedFilesProvider instrumentedFilesProvider = + InstrumentedFilesCollector.collect( + ruleContext, + instrumentationSpec, + InstrumentedFilesCollector.NO_METADATA_COLLECTOR, + Collections.<Artifact>emptySet()); + builder.addProvider(InstrumentedFilesProvider.class, instrumentedFilesProvider); + } else if (registeredProviderTypes.containsKey(key)) { + Class<? extends TransitiveInfoProvider> providerType = registeredProviderTypes.get(key); + TransitiveInfoProvider providerField = cast(key, provider, providerType, loc); + builder.addProvider(providerType, providerField); + } else if (isDefaultProvider) { + // Custom keys are not allowed for default providers + throw new EvalException(loc, "Invalid key for default provider: " + key); + } else if (key.equals("providers")) { + Iterable iterable = cast(key, provider, Iterable.class, loc); + for (Object o : iterable) { + SkylarkClassObject declaredProvider = SkylarkType.cast(o, SkylarkClassObject.class, loc, + "The value of 'providers' should be a sequence of declared providers"); + builder.addSkylarkDeclaredProvider(declaredProvider, loc); + } + } else if (!key.equals("executable")) { + // We handled executable already. + builder.addSkylarkTransitiveInfo(key, provider.getValue(key), loc); + } + } + + addSimpleProviders(builder, ruleContext, loc, executable, statelessRunfiles, dataRunfiles, + defaultRunfiles, (isDefaultProvider ? provider : null)); + } + + private static void addSimpleProviders(RuleConfiguredTargetBuilder builder, + RuleContext ruleContext, + Location loc, + Artifact executable, + Runfiles statelessRunfiles, + Runfiles dataRunfiles, + Runfiles defaultRunfiles, + SkylarkClassObject defaultProvider) throws EvalException { + if ((statelessRunfiles != null) && (dataRunfiles != null || defaultRunfiles != null)) { throw new EvalException(loc, "Cannot specify the provider 'runfiles' " + "together with 'data_runfiles' or 'default_runfiles'"); @@ -354,11 +403,16 @@ public final class SkylarkRuleConfiguredTargetBuilder { builder.addSkylarkDeclaredProvider(actions, loc); } - try { - return builder.build(); - } catch (IllegalArgumentException e) { - throw new EvalException(loc, e.getMessage()); - } + // Populate default provider fields and build it + ImmutableMap.Builder<String, Object> attrBuilder = new ImmutableMap.Builder<>(); + // TODO: Add actual attributes that users expect to access from default providers + attrBuilder.put("runfiles", runfilesProvider); + SkylarkClassObject statelessDefaultProvider = SkylarkRuleContext.getDefaultProvider().create( + attrBuilder.build(), "Default provider has no attribute '%s'"); + + // Add the default provider + builder.addSkylarkDeclaredProvider(statelessDefaultProvider, (defaultProvider == null) ? loc + : Optional.fromNullable(defaultProvider.getCreationLocOrNull()).or(loc)); } private static <T> T cast(String paramName, ClassObject struct, Class<T> expectedGenericType, diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java index 37a77d4be2..ebd4db681c 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java @@ -422,6 +422,14 @@ public final class SkylarkRuleContext { return ruleContext; } + private static final SkylarkClassObjectConstructor DEFAULT_PROVIDER = + SkylarkClassObjectConstructor.createNativeConstructable("default_provider"); + + @SkylarkCallable(name = "default_provider", structField = true) + public static SkylarkClassObjectConstructor getDefaultProvider() { + return DEFAULT_PROVIDER; + } + @SkylarkCallable(name = "created_actions", doc = "For rules marked <code>_skylark_testable=True</code>, this returns an " + "<a href=\"ActionsSkylarkApiProvider.html\">actions</a> provider representing all " |