From 06072c557290b97cb2d2e4fd255b647a9208cb5c Mon Sep 17 00:00:00 2001 From: Adam Michael Date: Wed, 1 Feb 2017 00:42:25 +0000 Subject: Make android_sdk_repository discover all system-images directories in the sdk. This will be used by the upcoming android_device rule that launches emulators. -- PiperOrigin-RevId: 146183077 MOS_MIGRATED_REVID=146183077 --- .../android/AndroidSdkRepositoryFunction.java | 112 +++++++++++++++++++-- .../android/android_sdk_repository_template.txt | 10 +- .../rules/android/AndroidSdkRepositoryTest.java | 35 +++++-- 3 files changed, 143 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryFunction.java index 179f5b51a4..2ce8b4c332 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryFunction.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryFunction.java @@ -14,10 +14,14 @@ package com.google.devtools.build.lib.bazel.rules.android; import com.android.repository.Revision; +import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import com.google.common.collect.Maps.EntryTransformer; import com.google.devtools.build.lib.analysis.BlazeDirectories; import com.google.devtools.build.lib.analysis.RuleDefinition; import com.google.devtools.build.lib.packages.Rule; @@ -41,6 +45,8 @@ import com.google.devtools.build.skyframe.SkyFunction.Environment; import com.google.devtools.build.skyframe.SkyFunctionException; import com.google.devtools.build.skyframe.SkyFunctionException.Transience; import com.google.devtools.build.skyframe.SkyKey; +import com.google.devtools.build.skyframe.SkyValue; +import com.google.devtools.build.skyframe.ValueOrException; import java.io.IOException; import java.util.Map; import java.util.Properties; @@ -52,8 +58,9 @@ import javax.annotation.Nullable; * Implementation of the {@code android_sdk_repository} rule. */ public class AndroidSdkRepositoryFunction extends RepositoryFunction { - private static final String BUILD_TOOLS_DIR_NAME = "build-tools"; - private static final String PLATFORMS_DIR_NAME = "platforms"; + private static final PathFragment BUILD_TOOLS_DIR = new PathFragment("build-tools"); + private static final PathFragment PLATFORMS_DIR = new PathFragment("platforms"); + private static final PathFragment SYSTEM_IMAGES_DIR = new PathFragment("system-images"); private static final Pattern PLATFORMS_API_LEVEL_PATTERN = Pattern.compile("android-(\\d+)"); private static final Revision MIN_BUILD_TOOLS_REVISION = new Revision(24, 0, 3); private static final String PATH_ENV_VAR = "ANDROID_HOME"; @@ -91,7 +98,7 @@ public class AndroidSdkRepositoryFunction extends RepositoryFunction { } DirectoryListingValue platformsDirectoryValue = - getDirectoryListing(fs, androidSdkPath.getChild(PLATFORMS_DIR_NAME), env); + getDirectoryListing(androidSdkPath, PLATFORMS_DIR, env); if (platformsDirectoryValue == null) { return null; } @@ -131,7 +138,7 @@ public class AndroidSdkRepositoryFunction extends RepositoryFunction { // If the build_tools_version attribute is not explicitly set, we select the highest version // installed in the SDK. DirectoryListingValue directoryValue = - getDirectoryListing(fs, androidSdkPath.getChild(BUILD_TOOLS_DIR_NAME), env); + getDirectoryListing(androidSdkPath, BUILD_TOOLS_DIR, env); if (directoryValue == null) { return null; } @@ -164,6 +171,17 @@ public class AndroidSdkRepositoryFunction extends RepositoryFunction { throw new RepositoryFunctionException(e, Transience.PERSISTENT); } + ImmutableSortedSet androidDeviceSystemImageDirs = + getAndroidDeviceSystemImageDirs(androidSdkPath, env); + if (androidDeviceSystemImageDirs == null) { + return null; + } + + StringBuilder systemImageDirsList = new StringBuilder(); + for (PathFragment systemImageDir : androidDeviceSystemImageDirs) { + systemImageDirsList.append(String.format(" \"%s\",\n", systemImageDir)); + } + String template = getStringResource("android_sdk_repository_template.txt"); String buildFile = template @@ -171,7 +189,8 @@ public class AndroidSdkRepositoryFunction extends RepositoryFunction { .replace("%build_tools_version%", buildToolsVersion) .replace("%build_tools_directory%", buildToolsDirectory) .replace("%api_levels%", Iterables.toString(apiLevels)) - .replace("%default_api_level%", defaultApiLevel); + .replace("%default_api_level%", defaultApiLevel) + .replace("%system_image_dirs%", systemImageDirsList); // All local maven repositories that are shipped in the Android SDK. // TODO(ajmichael): Create SkyKeys so that if the SDK changes, this function will get rerun. @@ -218,12 +237,12 @@ public class AndroidSdkRepositoryFunction extends RepositoryFunction { /** Gets a DirectoryListingValue for {@code dirPath} or returns null. */ private static DirectoryListingValue getDirectoryListing( - FileSystem fs, Path dirPath, Environment env) + Path root, PathFragment dirPath, Environment env) throws RepositoryFunctionException, InterruptedException { try { return (DirectoryListingValue) env.getValueOrThrow( - DirectoryListingValue.key(RootedPath.toRootedPath(fs.getRootDirectory(), dirPath)), + DirectoryListingValue.key(RootedPath.toRootedPath(root, dirPath)), InconsistentFilesystemException.class); } catch (InconsistentFilesystemException e) { throw new RepositoryFunctionException(new IOException(e), Transience.PERSISTENT); @@ -333,4 +352,83 @@ public class AndroidSdkRepositoryFunction extends RepositoryFunction { e); } } + + /** + * Gets PathFragments for /sdk/system-images/*/*/*, which are the directories in the + * SDK that contain system images needed for android_device. + */ + private static ImmutableSortedSet getAndroidDeviceSystemImageDirs( + Path androidSdkPath, Environment env) + throws RepositoryFunctionException, InterruptedException { + DirectoryListingValue systemImagesDirectoryValue = + getDirectoryListing(androidSdkPath, SYSTEM_IMAGES_DIR, env); + if (systemImagesDirectoryValue == null) { + return null; + } + ImmutableMap apiLevelSystemImageDirs = + getSubdirectoryListingValues( + androidSdkPath, SYSTEM_IMAGES_DIR, systemImagesDirectoryValue, env); + if (apiLevelSystemImageDirs == null) { + return null; + } + + ImmutableSortedSet.Builder pathFragments = ImmutableSortedSet.naturalOrder(); + for (PathFragment apiLevelDir : apiLevelSystemImageDirs.keySet()) { + ImmutableMap apiTypeSystemImageDirs = + getSubdirectoryListingValues( + androidSdkPath, apiLevelDir, apiLevelSystemImageDirs.get(apiLevelDir), env); + if (apiTypeSystemImageDirs == null) { + return null; + } + for (PathFragment apiTypeDir : apiTypeSystemImageDirs.keySet()) { + for (Dirent architectureSystemImageDir : + apiTypeSystemImageDirs.get(apiTypeDir).getDirents()) { + pathFragments.add(apiTypeDir.getRelative(architectureSystemImageDir.getName())); + } + } + } + return pathFragments.build(); + } + + /** Gets DirectoryListingValues for subdirectories of the directory or returns null. */ + private static ImmutableMap getSubdirectoryListingValues( + final Path root, final PathFragment path, DirectoryListingValue directory, Environment env) + throws RepositoryFunctionException, InterruptedException { + Map skyKeysForSubdirectoryLookups = + Maps.transformEntries( + Maps.uniqueIndex( + directory.getDirents(), + new Function() { + @Override + public PathFragment apply(Dirent input) { + return path.getRelative(input.getName()); + } + }), + new EntryTransformer() { + @Override + public SkyKey transformEntry(PathFragment key, Dirent value) { + return DirectoryListingValue.key( + RootedPath.toRootedPath(root, root.getRelative(key))); + } + }); + + Map> values = + env.getValuesOrThrow( + skyKeysForSubdirectoryLookups.values(), InconsistentFilesystemException.class); + + ImmutableMap.Builder directoryListingValues = + new ImmutableMap.Builder<>(); + for (PathFragment pathFragment : skyKeysForSubdirectoryLookups.keySet()) { + try { + SkyValue skyValue = values.get(skyKeysForSubdirectoryLookups.get(pathFragment)).get(); + if (skyValue == null) { + return null; + } + directoryListingValues.put(pathFragment, (DirectoryListingValue) skyValue); + } catch (InconsistentFilesystemException e) { + throw new RepositoryFunctionException(new IOException(e), Transience.PERSISTENT); + } + } + return directoryListingValues.build(); + } } diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android_sdk_repository_template.txt b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android_sdk_repository_template.txt index 41cc7cb9d2..33a251062a 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android_sdk_repository_template.txt +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android_sdk_repository_template.txt @@ -1,6 +1,9 @@ package(default_visibility = ["//visibility:public"]) -load("@bazel_tools//tools/android:android_sdk_repository_template.bzl", "create_android_sdk_rules") +load( + "@bazel_tools//tools/android:android_sdk_repository_template.bzl", + "create_android_device_rules", + "create_android_sdk_rules") create_android_sdk_rules( name = "%repository_name%", @@ -40,5 +43,10 @@ alias( actual = "tools/lib/emulator/snapshots.img", ) +create_android_device_rules( + system_image_dirs = [ +%system_image_dirs% ], +) + exports_files([ %exported_files%] + glob(["system-images/**"])) diff --git a/src/test/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryTest.java b/src/test/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryTest.java index 5d99bc6188..04799aa4bd 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryTest.java @@ -16,8 +16,11 @@ package com.google.devtools.build.lib.bazel.rules.android; import static com.google.common.truth.Truth.assertThat; +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.lib.analysis.FilesToRunProvider; import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; import com.google.devtools.build.lib.testutil.TestRuleClassProvider; import com.google.devtools.build.lib.vfs.FileSystemUtils; @@ -47,6 +50,9 @@ public class AndroidSdkRepositoryTest extends BuildViewTestCase { " 1.0.0", " aar", ""); + scratch.dir("system-images/android-25/default/armeabi-v7a"); + scratch.dir("system-images/android-24/google_apis/x86"); + scratch.dir("system-images/android-24/google_apis/x86_64"); scratch.setWorkingDir("/bazel_tools_workspace"); if (!scratch.resolve("WORKSPACE").exists()) { @@ -59,23 +65,28 @@ public class AndroidSdkRepositoryTest extends BuildViewTestCase { "tools/android/android_sdk_repository_template.bzl", "def create_android_sdk_rules(name, build_tools_version, build_tools_directory, ", " api_levels, default_api_level):", - " pass"); + " pass", + "def create_android_device_rules(system_image_dirs):", + " native.filegroup(", + " name = 'test_android_devices_filegroup',", + " srcs = system_image_dirs)"); scratch.setWorkingDir("/workspace"); FileSystemUtils.appendIsoLatin1(scratch.resolve("WORKSPACE"), "local_repository(name = 'bazel_tools', path = '/bazel_tools_workspace')", "android_sdk_repository(", - " name = 'mysdk',", + " name = 'androidsdk',", " path = '/sdk',", " build_tools_version = '25.0.0',", " api_level = 25,", ")"); + invalidatePackages(); } @Test public void testGeneratedAarImport() throws Exception { - invalidatePackages(); - ConfiguredTarget aarImportTarget = getConfiguredTarget("@mysdk//com.google.android:foo-1.0.0"); + ConfiguredTarget aarImportTarget = + getConfiguredTarget("@androidsdk//com.google.android:foo-1.0.0"); assertThat(aarImportTarget).isNotNull(); assertThat(aarImportTarget.getTarget().getAssociatedRule().getRuleClass()) .isEqualTo("aar_import"); @@ -83,9 +94,21 @@ public class AndroidSdkRepositoryTest extends BuildViewTestCase { @Test public void testExportsFiles() throws Exception { - invalidatePackages(); ConfiguredTarget aarTarget = getConfiguredTarget( - "@mysdk//:extras/google/m2repository/com/google/android/foo/1.0.0/foo.aar"); + "@androidsdk//:extras/google/m2repository/com/google/android/foo/1.0.0/foo.aar"); assertThat(aarTarget).isNotNull(); } + + @Test + public void testSystemImageDirectoriesAreFound() throws Exception { + ConfiguredTarget androidDevicesFilegroupTarget = + getConfiguredTarget("@androidsdk//:test_android_devices_filegroup"); + ImmutableList systemImagesDirectories = + androidDevicesFilegroupTarget.getProvider(FilesToRunProvider.class).getFilesToRun(); + assertThat(artifactsToStrings(systemImagesDirectories)) + .containsExactly( + "src external/androidsdk/system-images/android-25/default/armeabi-v7a", + "src external/androidsdk/system-images/android-24/google_apis/x86", + "src external/androidsdk/system-images/android-24/google_apis/x86_64"); + } } -- cgit v1.2.3