| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * 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.android.sdklib; |
| |
| import static com.android.SdkConstants.FD_LIB; |
| import static com.android.SdkConstants.FN_AAPT; |
| import static com.android.SdkConstants.FN_AIDL; |
| import static com.android.SdkConstants.FN_BCC_COMPAT; |
| import static com.android.SdkConstants.FN_DEXDUMP; |
| import static com.android.SdkConstants.FN_DX; |
| import static com.android.SdkConstants.FN_DX_JAR; |
| import static com.android.SdkConstants.FN_JACK; |
| import static com.android.SdkConstants.FN_JILL; |
| import static com.android.SdkConstants.FN_LD_ARM; |
| import static com.android.SdkConstants.FN_LD_MIPS; |
| import static com.android.SdkConstants.FN_LD_X86; |
| import static com.android.SdkConstants.FN_RENDERSCRIPT; |
| import static com.android.SdkConstants.FN_SPLIT_SELECT; |
| import static com.android.SdkConstants.FN_ZIPALIGN; |
| import static com.android.SdkConstants.OS_FRAMEWORK_RS; |
| import static com.android.SdkConstants.OS_FRAMEWORK_RS_CLANG; |
| import static com.android.sdklib.BuildToolInfo.PathId.AAPT; |
| import static com.android.sdklib.BuildToolInfo.PathId.AIDL; |
| import static com.android.sdklib.BuildToolInfo.PathId.ANDROID_RS; |
| import static com.android.sdklib.BuildToolInfo.PathId.ANDROID_RS_CLANG; |
| import static com.android.sdklib.BuildToolInfo.PathId.BCC_COMPAT; |
| import static com.android.sdklib.BuildToolInfo.PathId.DEXDUMP; |
| import static com.android.sdklib.BuildToolInfo.PathId.DX; |
| import static com.android.sdklib.BuildToolInfo.PathId.DX_JAR; |
| import static com.android.sdklib.BuildToolInfo.PathId.JACK; |
| import static com.android.sdklib.BuildToolInfo.PathId.JILL; |
| import static com.android.sdklib.BuildToolInfo.PathId.LD_ARM; |
| import static com.android.sdklib.BuildToolInfo.PathId.LD_MIPS; |
| import static com.android.sdklib.BuildToolInfo.PathId.LD_X86; |
| import static com.android.sdklib.BuildToolInfo.PathId.LLVM_RS_CC; |
| import static com.android.sdklib.BuildToolInfo.PathId.SPLIT_SELECT; |
| import static com.android.sdklib.BuildToolInfo.PathId.ZIP_ALIGN; |
| |
| import com.android.annotations.NonNull; |
| import com.android.annotations.Nullable; |
| import com.android.annotations.VisibleForTesting; |
| import com.android.annotations.VisibleForTesting.Visibility; |
| import com.android.sdklib.io.FileOp; |
| import com.android.sdklib.repository.FullRevision; |
| import com.android.sdklib.repository.NoPreviewRevision; |
| import com.android.utils.ILogger; |
| import com.google.common.collect.Maps; |
| |
| import java.io.File; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * Information on a specific build-tool folder. |
| * <p/> |
| * For unit tests, see: |
| * - sdklib/src/test/.../LocalSdkTest |
| * - sdklib/src/test/.../SdkManagerTest |
| * - sdklib/src/test/.../BuildToolInfoTest |
| */ |
| public class BuildToolInfo { |
| |
| /** Name of the file read by {@link #getRuntimeProps()} */ |
| private static final String FN_RUNTIME_PROPS = "runtime.properties"; |
| |
| /** |
| * Property in {@link #FN_RUNTIME_PROPS} indicating the desired runtime JVM. |
| * Type: {@link NoPreviewRevision#toShortString()}, e.g. "1.7.0" |
| */ |
| private static final String PROP_RUNTIME_JVM = "Runtime.Jvm"; |
| |
| /** |
| * First version with native multi-dex support. |
| */ |
| public static final int SDK_LEVEL_FOR_MULTIDEX_NATIVE_SUPPORT = 21; |
| |
| public enum PathId { |
| /** OS Path to the target's version of the aapt tool. */ |
| AAPT("1.0.0"), |
| /** OS Path to the target's version of the aidl tool. */ |
| AIDL("1.0.0"), |
| /** OS Path to the target's version of the dx tool. */ |
| DX("1.0.0"), |
| /** OS Path to the target's version of the dx.jar file. */ |
| DX_JAR("1.0.0"), |
| /** OS Path to the llvm-rs-cc binary for Renderscript. */ |
| LLVM_RS_CC("1.0.0"), |
| /** OS Path to the Renderscript include folder. */ |
| ANDROID_RS("1.0.0"), |
| /** OS Path to the Renderscript(clang) include folder. */ |
| ANDROID_RS_CLANG("1.0.0"), |
| |
| DEXDUMP("1.0.0"), |
| |
| // --- NEW IN 18.1.0 --- |
| |
| /** OS Path to the bcc_compat tool. */ |
| BCC_COMPAT("18.1.0"), |
| /** OS Path to the ARM linker. */ |
| LD_ARM("18.1.0"), |
| /** OS Path to the X86 linker. */ |
| LD_X86("18.1.0"), |
| /** OS Path to the MIPS linker. */ |
| LD_MIPS("18.1.0"), |
| |
| // --- NEW IN 19.1.0 --- |
| ZIP_ALIGN("19.1.0"), |
| |
| // --- NEW IN 21.x.y --- |
| JACK("21.1.0"), |
| JILL("21.1.0"), |
| |
| SPLIT_SELECT("22.0.0"); |
| |
| /** |
| * min revision this element was introduced. |
| * Controls {@link BuildToolInfo#isValid(ILogger)} |
| */ |
| private final FullRevision mMinRevision; |
| |
| /** |
| * Creates the enum with a min revision in which this |
| * tools appeared in the build tools. |
| * |
| * @param minRevision the min revision. |
| */ |
| PathId(@NonNull String minRevision) { |
| mMinRevision = FullRevision.parseRevision(minRevision); |
| } |
| |
| /** |
| * Returns whether the enum of present in a given rev of the build tools. |
| * |
| * @param fullRevision the build tools revision. |
| * @return true if the tool is present. |
| */ |
| boolean isPresentIn(@NonNull FullRevision fullRevision) { |
| return fullRevision.compareTo(mMinRevision) >= 0; |
| } |
| } |
| |
| /** The build-tool revision. */ |
| @NonNull |
| private final FullRevision mRevision; |
| |
| /** The path to the build-tool folder specific to this revision. */ |
| @NonNull |
| private final File mPath; |
| |
| private final Map<PathId, String> mPaths = Maps.newEnumMap(PathId.class); |
| |
| public BuildToolInfo(@NonNull FullRevision revision, @NonNull File path) { |
| mRevision = revision; |
| mPath = path; |
| |
| add(AAPT, FN_AAPT); |
| add(AIDL, FN_AIDL); |
| add(DX, FN_DX); |
| add(DX_JAR, FD_LIB + File.separator + FN_DX_JAR); |
| add(LLVM_RS_CC, FN_RENDERSCRIPT); |
| add(ANDROID_RS, OS_FRAMEWORK_RS); |
| add(ANDROID_RS_CLANG, OS_FRAMEWORK_RS_CLANG); |
| add(DEXDUMP, FN_DEXDUMP); |
| add(BCC_COMPAT, FN_BCC_COMPAT); |
| add(LD_ARM, FN_LD_ARM); |
| add(LD_X86, FN_LD_X86); |
| add(LD_MIPS, FN_LD_MIPS); |
| add(ZIP_ALIGN, FN_ZIPALIGN); |
| add(JACK, FN_JACK); |
| add(JILL, FN_JILL); |
| add(SPLIT_SELECT, FN_SPLIT_SELECT); |
| } |
| |
| public BuildToolInfo( |
| @NonNull FullRevision revision, |
| @NonNull File mainPath, |
| @NonNull File aapt, |
| @NonNull File aidl, |
| @NonNull File dx, |
| @NonNull File dxJar, |
| @NonNull File llmvRsCc, |
| @NonNull File androidRs, |
| @NonNull File androidRsClang, |
| @Nullable File bccCompat, |
| @Nullable File ldArm, |
| @Nullable File ldX86, |
| @Nullable File ldMips, |
| @NonNull File zipAlign) { |
| mRevision = revision; |
| mPath = mainPath; |
| add(AAPT, aapt); |
| add(AIDL, aidl); |
| add(DX, dx); |
| add(DX_JAR, dxJar); |
| add(LLVM_RS_CC, llmvRsCc); |
| add(ANDROID_RS, androidRs); |
| add(ANDROID_RS_CLANG, androidRsClang); |
| add(ZIP_ALIGN, zipAlign); |
| |
| if (bccCompat != null) { |
| add(BCC_COMPAT, bccCompat); |
| } else if (BCC_COMPAT.isPresentIn(revision)) { |
| throw new IllegalArgumentException("BCC_COMPAT required in " + revision.toString()); |
| } |
| if (ldArm != null) { |
| add(LD_ARM, ldArm); |
| } else if (LD_ARM.isPresentIn(revision)) { |
| throw new IllegalArgumentException("LD_ARM required in " + revision.toString()); |
| } |
| |
| if (ldX86 != null) { |
| add(LD_X86, ldX86); |
| } else if (LD_X86.isPresentIn(revision)) { |
| throw new IllegalArgumentException("LD_X86 required in " + revision.toString()); |
| } |
| |
| if (ldMips != null) { |
| add(LD_MIPS, ldMips); |
| } else if (LD_MIPS.isPresentIn(revision)) { |
| throw new IllegalArgumentException("LD_MIPS required in " + revision.toString()); |
| } |
| } |
| |
| private void add(PathId id, String leaf) { |
| add(id, new File(mPath, leaf)); |
| } |
| |
| private void add(PathId id, File path) { |
| String str = path.getAbsolutePath(); |
| if (path.isDirectory() && str.charAt(str.length() - 1) != File.separatorChar) { |
| str += File.separatorChar; |
| } |
| mPaths.put(id, str); |
| } |
| |
| /** |
| * Returns the revision. |
| */ |
| @NonNull |
| public FullRevision getRevision() { |
| return mRevision; |
| } |
| |
| /** |
| * Returns the build-tool revision-specific folder. |
| * <p/> |
| * For compatibility reasons, use {@link #getPath(PathId)} if you need the path to a |
| * specific tool. |
| */ |
| @NonNull |
| public File getLocation() { |
| return mPath; |
| } |
| |
| /** |
| * Returns the path of a build-tool component. |
| * |
| * @param pathId the id representing the path to return. |
| * @return The absolute path for that tool, with a / separator if it's a folder. |
| * Null if the path-id is unknown. |
| */ |
| public String getPath(PathId pathId) { |
| assert pathId.isPresentIn(mRevision); |
| |
| return mPaths.get(pathId); |
| } |
| |
| /** |
| * Checks whether the build-tool is valid by verifying that the expected binaries |
| * are actually present. This checks that all known paths point to a valid file |
| * or directory. |
| * |
| * @param log An optional logger. If non-null, errors will be printed there. |
| * @return True if the build-tool folder contains all the expected tools. |
| */ |
| public boolean isValid(@Nullable ILogger log) { |
| for (Map.Entry<PathId, String> entry : mPaths.entrySet()) { |
| File f = new File(entry.getValue()); |
| // check if file is missing. It's only ok if the revision of the build-tools |
| // is lower than the min rev of the element. |
| if (!f.exists() && entry.getKey().isPresentIn(mRevision)) { |
| if (log != null) { |
| log.warning("Build-tool %1$s is missing %2$s at %3$s", //$NON-NLS-1$ |
| mRevision.toString(), |
| entry.getKey(), f.getAbsolutePath()); |
| } |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Parses the build-tools runtime.props file, if present. |
| * |
| * @return The properties from runtime.props if present, otherwise an empty properties set. |
| */ |
| @NonNull |
| public Properties getRuntimeProps() { |
| FileOp fop = new FileOp(); |
| return fop.loadProperties(new File(mPath, FN_RUNTIME_PROPS)); |
| } |
| |
| /** |
| * Checks whether this build-tools package can run on the current JVM. |
| * |
| * @return True if the build-tools package has a Runtime.Jvm property and it is lesser or |
| * equal to the current JVM version. |
| * False if the property is present and the requirement is not met. |
| * True if there's an error parsing either versions and the comparison cannot be made. |
| */ |
| public boolean canRunOnJvm() { |
| Properties props = getRuntimeProps(); |
| String required = props.getProperty(PROP_RUNTIME_JVM); |
| if (required == null) { |
| // No requirement ==> accepts. |
| return true; |
| } |
| try { |
| NoPreviewRevision requiredVersion = NoPreviewRevision.parseRevision(required); |
| NoPreviewRevision currentVersion = getCurrentJvmVersion(); |
| return currentVersion.compareTo(requiredVersion) >= 0; |
| |
| } catch (NumberFormatException ignore) { |
| // Either we failed to parse the property version or the running JVM version. |
| // Right now take the relaxed policy of accepting it if we can't compare. |
| return true; |
| } |
| } |
| |
| @VisibleForTesting(visibility=Visibility.PRIVATE) |
| @Nullable |
| protected NoPreviewRevision getCurrentJvmVersion() throws NumberFormatException { |
| String javav = System.getProperty("java.version"); //$NON-NLS-1$ |
| // java Version is typically in the form "1.2.3_45" and we just need to keep up to "1.2.3" |
| // since our revision numbers are in 3-parts form (1.2.3). |
| Pattern p = Pattern.compile("((\\d+)(\\.\\d+)?(\\.\\d+)?).*"); //$NON-NLS-1$ |
| Matcher m = p.matcher(javav); |
| if (m.matches()) { |
| return NoPreviewRevision.parseRevision(m.group(1)); |
| } |
| return null; |
| } |
| |
| /** |
| * Returns a debug representation suitable for unit-tests. |
| * Note that unit-tests need to clean up the paths to avoid inconsistent results. |
| */ |
| @Override |
| public String toString() { |
| StringBuilder builder = new StringBuilder(); |
| builder.append("<BuildToolInfo rev=").append(mRevision); //$NON-NLS-1$ |
| builder.append(", mPath=").append(mPath); //$NON-NLS-1$ |
| builder.append(", mPaths=").append(getPathString()); //$NON-NLS-1$ |
| builder.append(">"); //$NON-NLS-1$ |
| return builder.toString(); |
| } |
| |
| private String getPathString() { |
| StringBuilder sb = new StringBuilder("{"); |
| |
| for (Map.Entry<PathId, String> entry : mPaths.entrySet()) { |
| if (entry.getKey().isPresentIn(mRevision)) { |
| if (sb.length() > 1) { |
| sb.append(", "); |
| } |
| sb.append(entry.getKey()).append('=').append(entry.getValue()); |
| } |
| } |
| |
| sb.append('}'); |
| |
| return sb.toString(); |
| } |
| } |