blob: df913ff94ad29ae2d052b384035cf017450f8843 [file] [log] [blame]
/*
* 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();
}
}