diff options
Diffstat (limited to 'src/main')
5 files changed, 167 insertions, 61 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java b/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java index 60d274787a..32469b5764 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java @@ -375,7 +375,9 @@ public abstract class DependencyResolver { } /** - * Returns true if the rule's attribute triggers a split in this configuration. + * Returns the BuildOptions if the rule's attribute triggers a split in this configuration, or + * the empty collection if the attribute does not trigger a split transition or if the split + * transition does not apply. * * <p>Even though the attribute may have a split, splits don't have to apply in every * configuration (see {@link Attribute.SplitTransition#split}). diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java index d782ce3e4b..46610f40d1 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java @@ -25,6 +25,7 @@ import com.google.devtools.build.lib.packages.Attribute; import com.google.devtools.build.lib.packages.Attribute.AllowedValueSet; import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition; import com.google.devtools.build.lib.packages.Attribute.SkylarkComputedDefaultTemplate; +import com.google.devtools.build.lib.packages.Attribute.SplitTransition; import com.google.devtools.build.lib.packages.AttributeValueSource; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.SkylarkAspect; @@ -282,6 +283,8 @@ public final class SkylarkAttr { builder.cfg(ConfigurationTransition.DATA); } else if (trans.equals("host")) { builder.cfg(ConfigurationTransition.HOST); + } else if (trans instanceof SplitTransition<?>) { + builder.cfg((SplitTransition<?>) trans); } else if (!trans.equals("target")) { throw new EvalException(ast.getLocation(), "cfg must be either 'data', 'host', or 'target'."); 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 71aae066e3..3ed2301b93 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 @@ -15,6 +15,7 @@ package com.google.devtools.build.lib.rules; import com.google.common.base.Function; +import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap.Builder; @@ -69,9 +70,11 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import javax.annotation.Nullable; @@ -119,6 +122,14 @@ public final class SkylarkRuleContext { public static final String ATTR_DOC = "A struct to access the values of the attributes. The values are provided by " + "the user (if not, a default value is used)."; + public static final String SPLIT_ATTR_DOC = + "A struct to access the values of attributes with split configurations. If the attribute is " + + "a label list, the value of split_attr is a dict of the keys of the split (as strings) " + + "to lists of the ConfiguredTargets in that branch of the splitt. If the attribute is a " + + "label, then the value of split_attr is a dict of the keys of the split (as strings) " + + "to single ConfiguredTargets. Attributes with split configurations still appear in the " + + "attr struct, but their values will be single lists with all the branches of the split " + + "merged together."; public static final String OUTPUTS_DOC = "A <code>struct</code> containing all the output files." + " The struct is generated the following way:<br>" @@ -151,6 +162,7 @@ public final class SkylarkRuleContext { private final SkylarkDict<String, String> makeVariables; private final SkylarkRuleAttributesCollection attributesCollection; private final SkylarkRuleAttributesCollection ruleAttributesCollection; + private final SkylarkClassObject splitAttributes; // TODO(bazel-team): we only need this because of the css_binary rule. private final ImmutableMap<Artifact, Label> artifactsLabelMap; @@ -228,6 +240,7 @@ public final class SkylarkRuleContext { this.attributesCollection = buildAttributesCollection( attributes, ruleContext, attributeValueExtractorForRule(ruleContext)); + this.splitAttributes = buildSplitAttributeInfo(attributes, ruleContext); this.ruleAttributesCollection = null; } else { // ASPECT this.artifactsLabelMap = ImmutableMap.of(); @@ -237,6 +250,7 @@ public final class SkylarkRuleContext { ruleContext.getAspectAttributes().values(), ruleContext, ATTRIBUTE_VALUE_EXTRACTOR_FOR_ASPECT); + this.splitAttributes = null; this.ruleAttributesCollection = buildAttributesCollection( ruleContext.getRule().getAttributes(), @@ -338,6 +352,52 @@ public final class SkylarkRuleContext { executableRunfilesbuilder.build()); } + private static SkylarkClassObject buildSplitAttributeInfo( + Collection<Attribute> attributes, RuleContext ruleContext) { + + ImmutableMap.Builder<String, Object> splitAttrInfos = ImmutableMap.builder(); + for (Attribute attr : attributes) { + + if (attr.hasSplitConfigurationTransition()) { + + Map<Optional<String>, ? extends List<? extends TransitiveInfoCollection>> splitPrereqs = + ruleContext.getSplitPrerequisites(attr.getName()); + + Map<Object, Object> splitPrereqsMap = new LinkedHashMap<>(); + for (Entry<Optional<String>, ? extends List<? extends TransitiveInfoCollection>> splitPrereq + : splitPrereqs.entrySet()) { + + Object value; + if (attr.getType() == BuildType.LABEL) { + Preconditions.checkState(splitPrereq.getValue().size() == 1); + value = splitPrereq.getValue().get(0); + } else { + // BuildType.LABEL_LIST + value = SkylarkList.createImmutable(splitPrereq.getValue()); + } + + if (splitPrereq.getKey().isPresent()) { + splitPrereqsMap.put(splitPrereq.getKey().get(), value); + } else { + // If the split transition is not in effect, then the key will be missing since there's + // nothing to key on because the dependencies aren't split and getSplitPrerequisites() + // behaves like getPrerequisites(). This also means there should be only one entry in + // the map. Use None in Skylark to represent this. + Preconditions.checkState(splitPrereqs.size() == 1); + splitPrereqsMap.put(Runtime.NONE, value); + } + } + + splitAttrInfos.put(attr.getPublicName(), SkylarkDict.copyOf(null, splitPrereqsMap)); + } + } + + return SkylarkClassObjectConstructor.STRUCT.create( + splitAttrInfos.build(), + "No attribute '%s' in split_attr. Make sure that this attribute is defined with a " + + "split configuration."); + } + @SkylarkModule( name = "rule_attributes", category = SkylarkModuleCategory.NONE, @@ -458,6 +518,15 @@ public final class SkylarkRuleContext { return attributesCollection.getAttr(); } + @SkylarkCallable(name = "split_attr", structField = true, doc = SPLIT_ATTR_DOC) + public SkylarkClassObject getSplitAttr() throws EvalException { + if (splitAttributes == null) { + throw new EvalException( + Location.BUILTIN, "'split_attr' is available only in rule implementations"); + } + return splitAttributes; + } + /** * <p>See {@link RuleContext#getExecutablePrerequisite(String, Mode)}. */ diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java index 4240fb22d0..6943e95cce 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java @@ -51,8 +51,9 @@ import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider; import com.google.devtools.build.lib.rules.java.JavaConfiguration; import com.google.devtools.build.lib.rules.java.JavaSemantics; import com.google.devtools.build.lib.rules.java.ProguardHelper; +import com.google.devtools.build.lib.skylarkinterface.SkylarkValue; +import com.google.devtools.build.lib.syntax.Printer; import com.google.devtools.build.lib.util.FileType; -import java.util.ArrayList; import java.util.List; import javax.annotation.Nullable; @@ -188,72 +189,90 @@ public final class AndroidRuleClasses { } public static final SplitTransition<BuildOptions> ANDROID_SPLIT_TRANSITION = - new SplitTransition<BuildOptions>() { - @Override - public boolean defaultsToSelf() { - return true; - } + new AndroidSplitTransition(); + + private static final class AndroidSplitTransition implements + SplitTransition<BuildOptions>, SkylarkValue { - private void setCrosstoolToAndroid(BuildOptions output, BuildOptions input) { - AndroidConfiguration.Options inputAndroidOptions = - input.get(AndroidConfiguration.Options.class); - AndroidConfiguration.Options outputAndroidOptions = - output.get(AndroidConfiguration.Options.class); - - CppOptions cppOptions = output.get(CppOptions.class); - if (inputAndroidOptions.androidCrosstoolTop != null - && !cppOptions.crosstoolTop.equals(inputAndroidOptions.androidCrosstoolTop)) { - if (cppOptions.hostCrosstoolTop == null) { - cppOptions.hostCrosstoolTop = cppOptions.crosstoolTop; - } - cppOptions.crosstoolTop = inputAndroidOptions.androidCrosstoolTop; - } + @Override + public boolean defaultsToSelf() { + return true; + } - outputAndroidOptions.configurationDistinguisher = ConfigurationDistinguisher.ANDROID; + private static void setCrosstoolToAndroid(BuildOptions output, BuildOptions input) { + AndroidConfiguration.Options inputAndroidOptions = + input.get(AndroidConfiguration.Options.class); + AndroidConfiguration.Options outputAndroidOptions = + output.get(AndroidConfiguration.Options.class); + + CppOptions cppOptions = output.get(CppOptions.class); + if (inputAndroidOptions.androidCrosstoolTop != null + && !cppOptions.crosstoolTop.equals(inputAndroidOptions.androidCrosstoolTop)) { + if (cppOptions.hostCrosstoolTop == null) { + cppOptions.hostCrosstoolTop = cppOptions.crosstoolTop; } + cppOptions.crosstoolTop = inputAndroidOptions.androidCrosstoolTop; + } - @Override - public List<BuildOptions> split(BuildOptions buildOptions) { - AndroidConfiguration.Options androidOptions = - buildOptions.get(AndroidConfiguration.Options.class); - CppOptions cppOptions = buildOptions.get(CppOptions.class); - Label androidCrosstoolTop = androidOptions.androidCrosstoolTop; - if (androidOptions.fatApkCpus.isEmpty() - && (androidCrosstoolTop == null - || androidCrosstoolTop.equals(cppOptions.crosstoolTop) - || androidOptions.cpu.isEmpty())) { - return ImmutableList.of(); - } + outputAndroidOptions.configurationDistinguisher = ConfigurationDistinguisher.ANDROID; + } - if (androidOptions.fatApkCpus.isEmpty()) { - BuildOptions splitOptions = buildOptions.clone(); - splitOptions.get(CppOptions.class).cppCompiler = androidOptions.cppCompiler; - // getSplitPrerequisites() will complain if cpu is null after this transition, - // so default to android_cpu. - splitOptions.get(BuildConfiguration.Options.class).cpu = androidOptions.cpu; - splitOptions.get(CppOptions.class).dynamicMode = androidOptions.dynamicMode; - setCrosstoolToAndroid(splitOptions, buildOptions); - return ImmutableList.of(splitOptions); - } + @Override + public List<BuildOptions> split(BuildOptions buildOptions) { - List<BuildOptions> result = new ArrayList<>(); - for (String cpu : ImmutableSortedSet.copyOf(androidOptions.fatApkCpus)) { - BuildOptions splitOptions = buildOptions.clone(); - // Disable fat APKs for the child configurations. - splitOptions.get(AndroidConfiguration.Options.class).fatApkCpus = ImmutableList.of(); - - // Set the cpu & android_cpu. - // TODO(bazel-team): --android_cpu doesn't follow --cpu right now; it should. - splitOptions.get(AndroidConfiguration.Options.class).cpu = cpu; - splitOptions.get(BuildConfiguration.Options.class).cpu = cpu; - splitOptions.get(CppOptions.class).cppCompiler = androidOptions.cppCompiler; - splitOptions.get(CppOptions.class).dynamicMode = androidOptions.dynamicMode; - setCrosstoolToAndroid(splitOptions, buildOptions); - result.add(splitOptions); - } - return result; + AndroidConfiguration.Options androidOptions = + buildOptions.get(AndroidConfiguration.Options.class); + CppOptions cppOptions = buildOptions.get(CppOptions.class); + Label androidCrosstoolTop = androidOptions.androidCrosstoolTop; + + if (androidOptions.fatApkCpus.isEmpty()) { + + if (androidOptions.cpu.isEmpty() + || androidCrosstoolTop == null + || androidCrosstoolTop.equals(cppOptions.crosstoolTop)) { + return ImmutableList.of(); + + } else { + + BuildOptions splitOptions = buildOptions.clone(); + splitOptions.get(CppOptions.class).cppCompiler = androidOptions.cppCompiler; + splitOptions.get(BuildConfiguration.Options.class).cpu = androidOptions.cpu; + splitOptions.get(CppOptions.class).dynamicMode = androidOptions.dynamicMode; + setCrosstoolToAndroid(splitOptions, buildOptions); + return ImmutableList.of(splitOptions); } - }; + + } else { + + ImmutableList.Builder<BuildOptions> result = ImmutableList.builder(); + for (String cpu : ImmutableSortedSet.copyOf(androidOptions.fatApkCpus)) { + BuildOptions splitOptions = buildOptions.clone(); + // Disable fat APKs for the child configurations. + splitOptions.get(AndroidConfiguration.Options.class).fatApkCpus = ImmutableList.of(); + + // Set the cpu & android_cpu. + // TODO(bazel-team): --android_cpu doesn't follow --cpu right now; it should. + splitOptions.get(AndroidConfiguration.Options.class).cpu = cpu; + splitOptions.get(BuildConfiguration.Options.class).cpu = cpu; + splitOptions.get(CppOptions.class).cppCompiler = androidOptions.cppCompiler; + splitOptions.get(CppOptions.class).dynamicMode = androidOptions.dynamicMode; + setCrosstoolToAndroid(splitOptions, buildOptions); + result.add(splitOptions); + } + return result.build(); + } + } + + @Override + public boolean isImmutable() { + return true; + } + + @Override + public void write(Appendable buffer, char quotationMark) { + Printer.append(buffer, "android_common.multi_cpu_configuration"); + } + } public static final FileType ANDROID_IDL = FileType.of(".aidl"); diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkCommon.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkCommon.java index 462407bcdb..1024b074e9 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkCommon.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkCommon.java @@ -14,6 +14,8 @@ package com.google.devtools.build.lib.rules.android; import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.config.BuildOptions; +import com.google.devtools.build.lib.packages.Attribute.SplitTransition; import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; import com.google.devtools.build.lib.vfs.PathFragment; @@ -26,6 +28,7 @@ import com.google.devtools.build.lib.vfs.PathFragment; doc = "Common utilities and fucntionality related to Android rules." ) public class AndroidSkylarkCommon { + @SkylarkCallable( name = "resource_source_directory", allowReturnNones = true, @@ -38,4 +41,14 @@ public class AndroidSkylarkCommon { public PathFragment getSourceDirectoryRelativePathFromResource(Artifact resource) { return AndroidCommon.getSourceDirectoryRelativePathFromResource(resource); } + + @SkylarkCallable( + name = "multi_cpu_configuration", + doc = "A configuration for rule attributes that compiles native code according to " + + "the --fat_apk_cpu and --android_crosstool_top flags.", + structField = true + ) + public SplitTransition<BuildOptions> getAndroidSplitTransition() { + return AndroidRuleClasses.ANDROID_SPLIT_TRANSITION; + } } |