aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java4
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java3
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java69
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java139
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkCommon.java13
-rw-r--r--src/test/java/com/google/devtools/build/lib/BUILD2
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/util/BazelMockAndroidSupport.java36
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/util/MOCK_ANDROID_CROSSTOOL126
-rw-r--r--src/test/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkSplitTransitionTest.java171
-rw-r--r--src/test/java/com/google/devtools/build/lib/rules/android/BUILD18
-rw-r--r--src/test/java/com/google/devtools/build/lib/skylark/BUILD1
11 files changed, 521 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;
+ }
}
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 40c40b9bf6..75e43c16ad 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -25,6 +25,7 @@ filegroup(
srcs = glob(["**"]) + [
"//src/test/java/com/google/devtools/build/lib/skylark:srcs",
"//src/test/java/com/google/devtools/build/lib/skyframe:srcs",
+ "//src/test/java/com/google/devtools/build/lib/rules/android:srcs",
"//src/test/java/com/google/devtools/build/lib/rules/repository:srcs",
"//src/test/java/com/google/devtools/build/lib/bazel/repository:srcs",
"//src/test/java/com/google/devtools/build/lib/buildeventstream/transports:srcs",
@@ -555,6 +556,7 @@ java_library(
srcs = glob([
"packages/util/*.java",
]),
+ resources = ["packages/util/MOCK_ANDROID_CROSSTOOL"],
deps = [
":foundations_testutil",
":testutil",
diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockAndroidSupport.java b/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockAndroidSupport.java
new file mode 100644
index 0000000000..99cba90ec3
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockAndroidSupport.java
@@ -0,0 +1,36 @@
+// Copyright 2016 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.packages.util;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.util.ResourceFileLoader;
+import java.io.IOException;
+
+/**
+ * Mocks out Android dependencies for testing.
+ */
+public class BazelMockAndroidSupport {
+
+ public static void setupNdk(MockToolsConfig config) throws IOException {
+ new Crosstool(config, "android/crosstool")
+ .setCrosstoolFile(
+ /*version=*/ "mock_version",
+ ResourceFileLoader.loadResource(
+ BazelMockAndroidSupport.class, "MOCK_ANDROID_CROSSTOOL"))
+ .setSupportedArchs(ImmutableList.of("x86", "armeabi-v7a"))
+ .setSupportsHeaderParsing(false)
+ .write();
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/MOCK_ANDROID_CROSSTOOL b/src/test/java/com/google/devtools/build/lib/packages/util/MOCK_ANDROID_CROSSTOOL
new file mode 100644
index 0000000000..f63bcb24e4
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/util/MOCK_ANDROID_CROSSTOOL
@@ -0,0 +1,126 @@
+major_version: "18"
+minor_version: "0"
+default_target_cpu: "armeabi-v7a"
+
+default_toolchain {
+ cpu: "armeabi-v7a"
+ toolchain_identifier: "armeabi-v7a"
+}
+
+default_toolchain {
+ cpu: "x86"
+ toolchain_identifier: "x86"
+}
+
+toolchain {
+ toolchain_identifier: "armeabi-v7a"
+ host_system_name: "x86"
+ target_system_name: "arm-linux-androideabi"
+
+ target_cpu: "armeabi-v7a"
+
+ target_libc: "local"
+ compiler: "gcc"
+ abi_version: "armeabi-v7a"
+ abi_libc_version: "r7"
+ tool_path {
+ name: "gcc"
+ path: "arm/bin/arm-linux-androideabi-gcc"
+ }
+ tool_path {
+ name: "ar"
+ path: "arm/bin/arm-linux-androideabi-ar"
+ }
+ tool_path {
+ name: "cpp"
+ path: "arm/bin/arm-linux-androideabi-cpp"
+ }
+ tool_path {
+ name: "gcov"
+ path: "arm/bin/arm-linux-androideabi-gcov"
+ }
+ tool_path {
+ name: "ld"
+ path: "arm/bin/arm-linux-androideabi-ld"
+ }
+ tool_path {
+ name: "nm"
+ path: "arm/bin/arm-linux-androideabi-nm"
+ }
+ tool_path {
+ name: "objcopy"
+ path: "arm/bin/arm-linux-androideabi-objcopy"
+ }
+ tool_path {
+ name: "objdump"
+ path: "arm/bin/arm-linux-androideabi-objdump"
+ }
+ tool_path {
+ name: "strip"
+ path: "arm/bin/arm-linux-androideabi-strip"
+ }
+ tool_path {
+ name: "ld-bfd"
+ path: "arm/bin/arm-linux-androideabi-ld.bfd"
+ }
+ tool_path {
+ name: "ld-gold"
+ path: "arm/bin/arm-linux-androideabi-ld.gold"
+ }
+}
+
+toolchain {
+ toolchain_identifier: "x86"
+ host_system_name: "x86"
+ target_system_name: "x86-linux-android"
+
+ target_cpu: "x86"
+ target_libc: "local"
+ compiler: "gcc"
+ abi_version: "x86"
+ abi_libc_version: "r7"
+ tool_path {
+ name: "gcc"
+ path: "x86/bin/i686-linux-android-gcc"
+ }
+ tool_path {
+ name: "ar"
+ path: "x86/bin/i686-linux-android-ar"
+ }
+ tool_path {
+ name: "cpp"
+ path: "x86/bin/i686-linux-android-cpp"
+ }
+ tool_path {
+ name: "gcov"
+ path: "x86/bin/i686-linux-android-gcov"
+ }
+ tool_path {
+ name: "ld"
+ path: "x86/bin/i686-linux-android-ld"
+ }
+ tool_path {
+ name: "nm"
+ path: "x86/bin/i686-linux-android-nm"
+ }
+ tool_path {
+ name: "objcopy"
+ path: "x86/bin/i686-linux-android-objcopy"
+ }
+ tool_path {
+ name: "objdump"
+ path: "x86/bin/i686-linux-android-objdump"
+ }
+ tool_path {
+ name: "strip"
+ path: "x86/bin/i686-linux-android-strip"
+ }
+ tool_path {
+ name: "ld-bfd"
+ path: "x86/bin/i686-linux-android-ld.bfd"
+ }
+ tool_path {
+ name: "ld-gold"
+ path: "x86/bin/i686-linux-android-ld.gold"
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkSplitTransitionTest.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkSplitTransitionTest.java
new file mode 100644
index 0000000000..f5f4395fdb
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkSplitTransitionTest.java
@@ -0,0 +1,171 @@
+// Copyright 2016 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.rules.android;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.packages.util.BazelMockAndroidSupport;
+import com.google.devtools.build.lib.syntax.Runtime;
+import java.util.List;
+import java.util.Map;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AndroidSkylarkSplitTransitionTest extends BuildViewTestCase {
+
+ protected boolean keepGoing() {
+ return false;
+ }
+
+ private void writeAndroidSplitTransitionTestFiles() throws Exception {
+ scratch.file(
+ "test/skylark/my_rule.bzl",
+ "def impl(ctx): ",
+ " return struct(",
+ " split_attr_deps = ctx.split_attr.deps,",
+ " split_attr_dep = ctx.split_attr.dep,",
+ " k8_deps = ctx.split_attr.deps.get('k8', None),",
+ " attr_deps = ctx.attr.deps,",
+ " attr_dep = ctx.attr.dep)",
+ "my_rule = rule(",
+ " implementation = impl,",
+ " attrs = {",
+ " 'deps': attr.label_list(cfg = android_common.multi_cpu_configuration),",
+ " 'dep': attr.label(cfg = android_common.multi_cpu_configuration),",
+ " })");
+
+ scratch.file(
+ "test/skylark/BUILD",
+ "load('/test/skylark/my_rule', 'my_rule')",
+ "my_rule(name = 'test', deps = [':main1', ':main2'], dep = ':main1')",
+ "cc_binary(name = 'main1', srcs = ['main1.c'])",
+ "cc_binary(name = 'main2', srcs = ['main2.c'])");
+ }
+
+ @Test
+ public void testAndroidSplitTransition() throws Exception {
+ writeAndroidSplitTransitionTestFiles();
+
+ useConfiguration("--fat_apk_cpu=k8,armeabi-v7a");
+ ConfiguredTarget target = getConfiguredTarget("//test/skylark:test");
+
+ // Check that ctx.split_attr.deps has this structure:
+ // {
+ // "k8": [ConfiguredTarget],
+ // "armeabi-v7a": [ConfiguredTarget],
+ // }
+ @SuppressWarnings("unchecked")
+ Map<String, List<ConfiguredTarget>> splitDeps =
+ (Map<String, List<ConfiguredTarget>>) target.get("split_attr_deps");
+ assertThat(splitDeps).containsKey("k8");
+ assertThat(splitDeps).containsKey("armeabi-v7a");
+ assertThat(splitDeps.get("k8")).hasSize(2);
+ assertThat(splitDeps.get("armeabi-v7a")).hasSize(2);
+ assertThat(splitDeps.get("k8").get(0).getConfiguration().getCpu()).isEqualTo("k8");
+ assertThat(splitDeps.get("k8").get(1).getConfiguration().getCpu()).isEqualTo("k8");
+ assertThat(splitDeps.get("armeabi-v7a").get(0).getConfiguration().getCpu())
+ .isEqualTo("armeabi-v7a");
+ assertThat(splitDeps.get("armeabi-v7a").get(1).getConfiguration().getCpu())
+ .isEqualTo("armeabi-v7a");
+
+ // Check that ctx.split_attr.dep has this structure (that is, that the values are not lists):
+ // {
+ // "k8": ConfiguredTarget,
+ // "armeabi-v7a": ConfiguredTarget,
+ // }
+ @SuppressWarnings("unchecked")
+ Map<String, ConfiguredTarget> splitDep =
+ (Map<String, ConfiguredTarget>) target.get("split_attr_dep");
+ assertThat(splitDep).containsKey("k8");
+ assertThat(splitDep).containsKey("armeabi-v7a");
+ assertThat(splitDep.get("k8").getConfiguration().getCpu()).isEqualTo("k8");
+ assertThat(splitDep.get("armeabi-v7a").getConfiguration().getCpu()).isEqualTo("armeabi-v7a");
+
+ // The regular ctx.attr.deps should be a single list with all the branches of the split merged
+ // together (i.e. for aspects).
+ @SuppressWarnings("unchecked")
+ List<ConfiguredTarget> attrDeps = (List<ConfiguredTarget>) target.get("attr_deps");
+ assertThat(attrDeps).hasSize(4);
+ ListMultimap<String, Object> attrDepsMap = ArrayListMultimap.create();
+ for (ConfiguredTarget ct : attrDeps) {
+ attrDepsMap.put(ct.getConfiguration().getCpu(), target);
+ }
+ assertThat(attrDepsMap.get("k8")).hasSize(2);
+ assertThat(attrDepsMap.get("armeabi-v7a")).hasSize(2);
+
+ // Check that even though my_rule.dep is defined as a single label, ctx.attr.dep is still a list
+ // with multiple ConfiguredTarget objects because of the two different CPUs.
+ @SuppressWarnings("unchecked")
+ List<ConfiguredTarget> attrDep = (List<ConfiguredTarget>) target.get("attr_dep");
+ assertThat(attrDep).hasSize(2);
+ ListMultimap<String, Object> attrDepMap = ArrayListMultimap.create();
+ for (ConfiguredTarget ct : attrDep) {
+ attrDepMap.put(ct.getConfiguration().getCpu(), target);
+ }
+ assertThat(attrDepMap.get("k8")).hasSize(1);
+ assertThat(attrDepMap.get("armeabi-v7a")).hasSize(1);
+
+ // Check that the deps were correctly accessed from within Skylark.
+ @SuppressWarnings("unchecked")
+ List<ConfiguredTarget> k8Deps = (List<ConfiguredTarget>) target.get("k8_deps");
+ assertThat(k8Deps).hasSize(2);
+ assertThat(k8Deps.get(0).getConfiguration().getCpu()).isEqualTo("k8");
+ assertThat(k8Deps.get(1).getConfiguration().getCpu()).isEqualTo("k8");
+ }
+
+ @Test
+ public void testAndroidSplitTransitionWithAndroidCpu() throws Exception {
+ writeAndroidSplitTransitionTestFiles();
+ BazelMockAndroidSupport.setupNdk(mockToolsConfig);
+
+ // --android_cpu with --android_crosstool_top also triggers the split transition.
+ useConfiguration(
+ "--android_cpu=armeabi-v7a", "--android_crosstool_top=//android/crosstool:everything");
+ ConfiguredTarget target = getConfiguredTarget("//test/skylark:test");
+
+ @SuppressWarnings("unchecked")
+ Map<Object, List<ConfiguredTarget>> splitDeps =
+ (Map<Object, List<ConfiguredTarget>>) target.get("split_attr_deps");
+
+ String cpu = "armeabi-v7a";
+ assertThat(splitDeps.get(cpu)).hasSize(2);
+ assertThat(splitDeps.get(cpu).get(0).getConfiguration().getCpu()).isEqualTo(cpu);
+ assertThat(splitDeps.get(cpu).get(1).getConfiguration().getCpu()).isEqualTo(cpu);
+ }
+
+ @Test
+ public void testAndroidSplitTransitionNoTransition() throws Exception {
+ writeAndroidSplitTransitionTestFiles();
+
+ useConfiguration("--fat_apk_cpu=", "--android_crosstool_top=");
+ ConfiguredTarget target = getConfiguredTarget("//test/skylark:test");
+
+ @SuppressWarnings("unchecked")
+ Map<Object, List<ConfiguredTarget>> splitDeps =
+ (Map<Object, List<ConfiguredTarget>>) target.get("split_attr_deps");
+
+ // Split transition isn't in effect, so the deps are compiled normally (i.e. using --cpu).
+ assertThat(splitDeps.get(Runtime.NONE)).hasSize(2);
+ assertThat(splitDeps.get(Runtime.NONE).get(0).getConfiguration().getCpu()).isEqualTo("k8");
+ assertThat(splitDeps.get(Runtime.NONE).get(1).getConfiguration().getCpu()).isEqualTo("k8");
+ }
+
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/BUILD b/src/test/java/com/google/devtools/build/lib/rules/android/BUILD
new file mode 100644
index 0000000000..460be997be
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/android/BUILD
@@ -0,0 +1,18 @@
+filegroup(
+ name = "srcs",
+ srcs = glob(["**"]),
+ visibility = ["//src/test/java/com/google/devtools/build/lib:__pkg__"],
+)
+
+java_test(
+ name = "AndroidSkylarkSplitTransitionTest",
+ srcs = ["AndroidSkylarkSplitTransitionTest.java"],
+ deps = [
+ "//src/main/java/com/google/devtools/build/lib:build-base",
+ "//src/main/java/com/google/devtools/build/lib:syntax",
+ "//src/test/java/com/google/devtools/build/lib:analysis_testutil",
+ "//src/test/java/com/google/devtools/build/lib:packages_testutil",
+ "//third_party:guava",
+ "//third_party:truth",
+ ],
+)
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/BUILD b/src/test/java/com/google/devtools/build/lib/skylark/BUILD
index a5fa37d9d6..85c00a305c 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/skylark/BUILD
@@ -59,6 +59,7 @@ java_test(
"//src/test/java/com/google/devtools/build/lib:actions_testutil",
"//src/test/java/com/google/devtools/build/lib:analysis_testutil",
"//src/test/java/com/google/devtools/build/lib:foundations_testutil",
+ "//src/test/java/com/google/devtools/build/lib:packages_testutil",
"//src/test/java/com/google/devtools/build/lib:test_runner",
"//src/test/java/com/google/devtools/build/lib:testutil",
"//third_party:guava",