Java tutorial
/* * Copyright 2018-present Facebook, Inc. * * 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.facebook.buck.android.toolchain.impl; import com.facebook.buck.android.toolchain.AndroidBuildToolsLocation; import com.facebook.buck.android.toolchain.AndroidPlatformTarget; import com.facebook.buck.android.toolchain.AndroidSdkLocation; import com.facebook.buck.core.exceptions.HumanReadableException; import com.facebook.buck.core.sourcepath.PathSourcePath; import com.facebook.buck.core.toolchain.tool.Tool; import com.facebook.buck.core.toolchain.tool.impl.VersionedTool; import com.facebook.buck.io.filesystem.ProjectFilesystem; import com.facebook.buck.util.environment.Platform; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import java.io.File; import java.io.FilenameFilter; import java.nio.file.Path; import java.util.Arrays; import java.util.Comparator; import java.util.LinkedList; import java.util.Optional; import java.util.Set; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; public class AndroidPlatformTargetProducer { @VisibleForTesting static final Pattern PLATFORM_TARGET_PATTERN = Pattern.compile("(?:Google Inc\\.:Google APIs:|android-)(.+)"); /** * @param platformId for the platform, such as "Google Inc.:Google APIs:16" * @param aaptOverride * @param aapt2Override */ public static AndroidPlatformTarget getTargetForId(ProjectFilesystem filesystem, String platformId, AndroidBuildToolsLocation androidBuildToolsLocation, AndroidSdkLocation androidSdkLocation, Optional<Supplier<Tool>> aaptOverride, Optional<Supplier<Tool>> aapt2Override) { Matcher platformMatcher = PLATFORM_TARGET_PATTERN.matcher(platformId); if (platformMatcher.matches()) { String apiLevel = platformMatcher.group(1); Factory platformTargetFactory; if (platformId.contains("Google APIs")) { platformTargetFactory = new AndroidWithGoogleApisFactory(); } else { platformTargetFactory = new AndroidWithoutGoogleApisFactory(); } return platformTargetFactory.newInstance(filesystem, androidBuildToolsLocation, androidSdkLocation, apiLevel, aaptOverride, aapt2Override); } else { String messagePrefix = String.format("The Android SDK for '%s' could not be found. ", platformId); throw new HumanReadableException(messagePrefix + "Must set ANDROID_SDK to point to the absolute path of your Android SDK directory."); } } public static AndroidPlatformTarget getDefaultPlatformTarget(ProjectFilesystem filesystem, AndroidBuildToolsLocation androidBuildToolsLocation, AndroidSdkLocation androidSdkLocation, Optional<Supplier<Tool>> aaptOverride, Optional<Supplier<Tool>> aapt2Override) { return getTargetForId(filesystem, AndroidPlatformTarget.DEFAULT_ANDROID_PLATFORM_TARGET, androidBuildToolsLocation, androidSdkLocation, aaptOverride, aapt2Override); } private interface Factory { AndroidPlatformTarget newInstance(ProjectFilesystem filesystem, AndroidBuildToolsLocation androidBuildToolsLocation, AndroidSdkLocation androidSdkLocation, String apiLevel, Optional<Supplier<Tool>> aaptOverride, Optional<Supplier<Tool>> aapt2Override); } /** * Given the path to the Android SDK as well as the platform path within the Android SDK, find all * the files needed to create the {@link AndroidPlatformTarget}, assuming that the organization of * the Android SDK conforms to the ordinary directory structure. */ @VisibleForTesting static AndroidPlatformTarget createFromDefaultDirectoryStructure(ProjectFilesystem filesystem, String name, AndroidBuildToolsLocation androidBuildToolsLocation, AndroidSdkLocation androidSdkLocation, String platformDirectoryPath, Set<Path> additionalJarPaths, Optional<Supplier<Tool>> aaptOverride, Optional<Supplier<Tool>> aapt2Override) { Path androidSdkDir = androidSdkLocation.getSdkRootPath(); if (!androidSdkDir.isAbsolute()) { throw new HumanReadableException("Path to Android SDK must be absolute but was: %s.", androidSdkDir); } Path platformDirectory = androidSdkDir.resolve(platformDirectoryPath); Path androidJar = platformDirectory.resolve("android.jar"); // Add any libraries found in the optional directory under the Android SDK directory. These // go at the head of the bootclasspath before any additional jars. File optionalDirectory = platformDirectory.resolve("optional").toFile(); if (optionalDirectory.exists() && optionalDirectory.isDirectory()) { String[] optionalDirList = optionalDirectory.list(new AddonFilter()); if (optionalDirList != null) { Arrays.sort(optionalDirList); ImmutableSet.Builder<Path> additionalJars = ImmutableSet.builder(); for (String file : optionalDirList) { additionalJars.add(optionalDirectory.toPath().resolve(file)); } additionalJars.addAll(additionalJarPaths); additionalJarPaths = additionalJars.build(); } } LinkedList<Path> bootclasspathEntries = Lists.newLinkedList(additionalJarPaths); // Make sure android.jar is at the front of the bootclasspath. bootclasspathEntries.addFirst(androidJar); // This is the directory under the Android SDK directory that contains the dx script, jack, // jill, and binaries. Path buildToolsDir = androidBuildToolsLocation.getBuildToolsPath(); Path buildToolsBinDir = androidBuildToolsLocation.getBuildToolsBinPath(); String version = buildToolsDir.getFileName().toString(); Path zipAlignExecutable = androidSdkDir.resolve("tools/zipalign").toAbsolutePath(); if (!zipAlignExecutable.toFile().exists()) { // Android SDK Build-tools >= 19.1.0 have zipalign under the build-tools directory. zipAlignExecutable = androidSdkDir.resolve(buildToolsBinDir).resolve("zipalign").toAbsolutePath(); } Path androidFrameworkIdlFile = platformDirectory.resolve("framework.aidl"); Path proguardJar = androidSdkDir.resolve("tools/proguard/lib/proguard.jar"); Path proguardConfig = androidSdkDir.resolve("tools/proguard/proguard-android.txt"); Path optimizedProguardConfig = androidSdkDir.resolve("tools/proguard/proguard-android-optimize.txt"); return AndroidPlatformTarget .of(name, androidJar.toAbsolutePath(), bootclasspathEntries, aaptOverride .orElse(() -> VersionedTool .of(PathSourcePath.of(filesystem, androidSdkDir.resolve(androidBuildToolsLocation.getAaptPath()) .toAbsolutePath()), "aapt", version)), aapt2Override.orElse(() -> VersionedTool.of(PathSourcePath.of(filesystem, androidSdkDir.resolve(androidBuildToolsLocation.getAapt2Path()).toAbsolutePath()), "aapt2", version)), androidSdkDir.resolve("platform-tools/adb").toAbsolutePath(), androidSdkDir.resolve(buildToolsBinDir).resolve("aidl").toAbsolutePath(), zipAlignExecutable, buildToolsDir.resolve(Platform.detect() == Platform.WINDOWS ? "dx.bat" : "dx") .toAbsolutePath(), androidFrameworkIdlFile, proguardJar, proguardConfig, optimizedProguardConfig); } /** Factory to build an AndroidPlatformTarget that corresponds to a given Google API level. */ private static class AndroidWithGoogleApisFactory implements Factory { private static final String API_DIR_SUFFIX = "(?:-([0-9]+))*"; @Override public AndroidPlatformTarget newInstance(ProjectFilesystem filesystem, AndroidBuildToolsLocation androidBuildToolsLocation, AndroidSdkLocation androidSdkLocation, String apiLevel, Optional<Supplier<Tool>> aaptOverride, Optional<Supplier<Tool>> aapt2Override) { // TODO(natthu): Use Paths instead of Strings everywhere in this file. Path androidSdkDir = androidSdkLocation.getSdkRootPath(); File addonsParentDir = androidSdkDir.resolve("add-ons").toFile(); String apiDirPrefix = "addon-google_apis-google-" + apiLevel; Pattern apiDirPattern = Pattern.compile(apiDirPrefix + API_DIR_SUFFIX); if (addonsParentDir.isDirectory()) { String[] addonsApiDirs = addonsParentDir .list((dir, name1) -> apiDirPattern.matcher(name1).matches()); Arrays.sort(addonsApiDirs, new Comparator<String>() { @Override public int compare(String o1, String o2) { return getVersion(o1) - getVersion(o2); } private int getVersion(String dirName) { Matcher matcher = apiDirPattern.matcher(dirName); Preconditions.checkState(matcher.matches()); if (matcher.group(1) != null) { return Integer.parseInt(matcher.group(1)); } return 0; } }); ImmutableSet.Builder<Path> additionalJarPaths = ImmutableSet.builder(); for (String dir : addonsApiDirs) { File libsDir = new File(addonsParentDir, dir + "/libs"); String[] addonFiles; if (libsDir.isDirectory() && (addonFiles = libsDir.list(new AddonFilter())) != null && addonFiles.length != 0) { Arrays.sort(addonFiles); for (String addonJar : addonFiles) { additionalJarPaths.add(libsDir.toPath().resolve(addonJar)); } return createFromDefaultDirectoryStructure(filesystem, "Google Inc.:Google APIs:" + apiLevel, androidBuildToolsLocation, androidSdkLocation, "platforms/android-" + apiLevel, additionalJarPaths.build(), aaptOverride, aapt2Override); } } } throw new HumanReadableException( "Google APIs not found in %s.\n" + "Please run '%s/tools/android sdk' and select both 'SDK Platform' and " + "'Google APIs' under Android (API %s)", new File(addonsParentDir, apiDirPrefix + "/libs").getAbsolutePath(), androidSdkDir, apiLevel); } } private static class AndroidWithoutGoogleApisFactory implements Factory { @Override public AndroidPlatformTarget newInstance(ProjectFilesystem filesystem, AndroidBuildToolsLocation androidBuildToolsLocation, AndroidSdkLocation androidSdkLocation, String apiLevel, Optional<Supplier<Tool>> aaptOverride, Optional<Supplier<Tool>> aapt2Override) { return createFromDefaultDirectoryStructure(filesystem, "android-" + apiLevel, androidBuildToolsLocation, androidSdkLocation, "platforms/android-" + apiLevel, /* additionalJarPaths */ ImmutableSet.of(), aaptOverride, aapt2Override); } } private static class AddonFilter implements FilenameFilter { @Override public boolean accept(File dir, String name) { return name.endsWith(".jar"); } } }