am 6502a1b2: Merge "Add support for build-tools in ADT."

* commit '6502a1b2a4a41eb1699ad29df3e5ec8df4fb2800':
  Add support for build-tools in ADT.
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
index 6bd16ee..77108c8 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
@@ -81,6 +81,13 @@
         <persistent value="false" />
     </extension>
     <extension
+        id="com.android.ide.eclipse.adt.buildToolsProblem"
+        name="Android Build Tools Problem"
+        point="org.eclipse.core.resources.markers">
+        <super type="org.eclipse.core.resources.problemmarker" />
+        <persistent value="false" />
+    </extension>
+    <extension
         id="com.android.ide.eclipse.adt.dependencyProblem"
         name="Android Dependency Problem"
         point="org.eclipse.core.resources.markers">
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java
index edfc30c..76808e4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java
@@ -151,6 +151,10 @@
      * when an AndroidClasspathContainerInitializer has succeeded in creating an
      * AndroidClasspathContainer */
     public final static String MARKER_TARGET = AdtPlugin.PLUGIN_ID + ".targetProblem"; //$NON-NLS-1$
+    /** Marker for Android Build Tools errors.
+     * This is not cleared on each build like other markers. Instead, it's cleared
+     * when the build tools are setup in the projectState. */
+    public final static String MARKER_BUILD_TOOLS = AdtPlugin.PLUGIN_ID + ".buildToolsProblem"; //$NON-NLS-1$
     /** Marker for Android Dependency errors.
      * This is not cleared on each build like other markers. Instead, it's cleared
      * when a LibraryClasspathContainerInitializer has succeeded in creating a
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
index 88e9c15..7aec8f5 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
@@ -1433,7 +1433,7 @@
                                     // project that have been resolved before the sdk was loaded
                                     // will have a ProjectState where the IAndroidTarget is null
                                     // so we load the target now that the SDK is loaded.
-                                    sdk.loadTarget(Sdk.getProjectState(iProject));
+                                    sdk.loadTargetAndBuildTools(Sdk.getProjectState(iProject));
                                     list.add(javaProject);
                                 }
                             }
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java
index 71c8263..9d33230 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java
@@ -20,15 +20,14 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.build.DexWrapper;
 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
 import com.android.ide.eclipse.adt.internal.sdk.AdtConsoleSdkLog;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
 import com.android.sdklib.io.FileOp;
+import com.android.sdklib.repository.ISdkChangeListener;
 import com.android.sdklib.util.GrabProcessOutput;
 import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;
 import com.android.sdklib.util.GrabProcessOutput.Wait;
-import com.android.sdklib.repository.ISdkChangeListener;
 import com.android.sdkuilib.repository.SdkUpdaterWindow;
 import com.android.sdkuilib.repository.SdkUpdaterWindow.SdkInvocationContext;
 
@@ -313,9 +312,7 @@
 
                 if (sdk != null) {
                     sdk.unloadTargetData(true /*preventReload*/);
-
-                    DexWrapper dx = sdk.getDexWrapper();
-                    dx.unload();
+                    sdk.unloadDexWrappers();
                 }
             }
 
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AidlProcessor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AidlProcessor.java
index fa878cf..806fa9c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AidlProcessor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AidlProcessor.java
@@ -17,12 +17,14 @@
 package com.android.ide.eclipse.adt.internal.build;
 
 import com.android.SdkConstants;
+import com.android.annotations.NonNull;
 import com.android.ide.eclipse.adt.AdtConstants;
 import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.internal.build.builders.BaseBuilder;
 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity;
 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
+import com.android.sdklib.BuildToolInfo;
 import com.android.sdklib.IAndroidTarget;
 import com.android.sdklib.io.FileOp;
 
@@ -81,8 +83,9 @@
 //          "^\\s*interface\\s+([a-zA-Z_][a-zA-Z0-9_]*)\\s*(?:\\{.*)?$");
 
 
-    public AidlProcessor(IJavaProject javaProject, IFolder genFolder) {
-        super(javaProject, genFolder);
+    public AidlProcessor(@NonNull IJavaProject javaProject, @NonNull BuildToolInfo buildToolInfo,
+            @NonNull IFolder genFolder) {
+        super(javaProject, buildToolInfo, genFolder);
     }
 
     @Override
@@ -95,7 +98,6 @@
         return PROPERTY_COMPILE_AIDL;
     }
 
-    @SuppressWarnings("deprecation")
     @Override
     protected void doCompileFiles(List<IFile> sources, BaseBuilder builder,
             IProject project, IAndroidTarget projectTarget,
@@ -104,7 +106,7 @@
         // create the command line
         List<String> commandList = new ArrayList<String>(
                 4 + sourceFolders.size() + libraryProjectsOut.size());
-        commandList.add(projectTarget.getPath(IAndroidTarget.AIDL));
+        commandList.add(getBuildToolInfo().getPath(BuildToolInfo.PathId.AIDL));
         commandList.add(quote("-p" + projectTarget.getPath(IAndroidTarget.ANDROID_AIDL))); //$NON-NLS-1$
 
         // since the path are relative to the workspace and not the project itself, we need
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
index c94ef9a..da8c2ea 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
@@ -27,6 +27,7 @@
 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
 import com.android.prefs.AndroidLocation.AndroidLocationException;
+import com.android.sdklib.BuildToolInfo;
 import com.android.sdklib.IAndroidTarget;
 import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
 import com.android.sdklib.build.ApkBuilder;
@@ -105,8 +106,13 @@
     private static final String COMMAND_CRUNCH = "crunch";  //$NON-NLS-1$
     private static final String COMMAND_PACKAGE = "package"; //$NON-NLS-1$
 
+    @NonNull
     private final IProject mProject;
+    @NonNull
+    private final BuildToolInfo mBuildToolInfo;
+    @NonNull
     private final AndroidPrintStream mOutStream;
+    @NonNull
     private final AndroidPrintStream mErrStream;
     private final boolean mForceJumbo;
     private final boolean mDisableDexMerger;
@@ -139,11 +145,13 @@
      * @throws CoreException
      */
     public BuildHelper(@NonNull IProject project,
+            @NonNull BuildToolInfo buildToolInfo,
             @NonNull AndroidPrintStream outStream,
             @NonNull AndroidPrintStream errStream,
             boolean forceJumbo, boolean disableDexMerger, boolean debugMode,
             boolean verbose, ResourceMarker resMarker) throws CoreException {
         mProject = project;
+        mBuildToolInfo = buildToolInfo;
         mOutStream = outStream;
         mErrStream = errStream;
         mDebugMode = debugMode;
@@ -693,7 +701,7 @@
 
         // get the dex wrapper
         Sdk sdk = Sdk.getCurrent();
-        DexWrapper wrapper = sdk.getDexWrapper();
+        DexWrapper wrapper = sdk.getDexWrapper(mBuildToolInfo);
 
         if (wrapper == null) {
             throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
@@ -833,7 +841,7 @@
             String configFilter, int versionCode) throws AaptExecException, AaptResultException {
         IAndroidTarget target = Sdk.getCurrent().getTarget(mProject);
 
-        @SuppressWarnings("deprecation") String aapt = target.getPath(IAndroidTarget.AAPT);
+        String aapt = mBuildToolInfo.getPath(BuildToolInfo.PathId.AAPT);
 
         // Create the command line.
         ArrayList<String> commandArray = new ArrayList<String>();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RenderScriptProcessor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RenderScriptProcessor.java
index 2652866..af58e41 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RenderScriptProcessor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RenderScriptProcessor.java
@@ -17,6 +17,7 @@
 package com.android.ide.eclipse.adt.internal.build;
 
 import com.android.SdkConstants;
+import com.android.annotations.NonNull;
 import com.android.ide.eclipse.adt.AdtConstants;
 import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.internal.build.builders.BaseBuilder;
@@ -25,6 +26,7 @@
 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
 import com.android.resources.ResourceFolderType;
+import com.android.sdklib.BuildToolInfo;
 import com.android.sdklib.IAndroidTarget;
 import com.google.common.collect.Sets;
 
@@ -145,8 +147,9 @@
 
     private int mTargetApi = 11;
 
-    public RenderScriptProcessor(IJavaProject javaProject, IFolder genFolder) {
-        super(javaProject, genFolder, new RsChangeHandler());
+    public RenderScriptProcessor(@NonNull IJavaProject javaProject,
+            @NonNull BuildToolInfo buildToolInfo, @NonNull IFolder genFolder) {
+        super(javaProject, buildToolInfo, genFolder, new RsChangeHandler());
     }
 
     public void setTargetApi(int targetApi) {
@@ -164,7 +167,6 @@
         return PROPERTY_COMPILE_RS;
     }
 
-    @SuppressWarnings("deprecation")
     @Override
     protected void doCompileFiles(List<IFile> sources, BaseBuilder builder,
             IProject project, IAndroidTarget projectTarget,
@@ -186,9 +188,9 @@
         command[index++] = quote(sdkOsPath + SdkConstants.OS_SDK_PLATFORM_TOOLS_FOLDER
                 + SdkConstants.FN_RENDERSCRIPT);
         command[index++] = "-I";   //$NON-NLS-1$
-        command[index++] = quote(projectTarget.getPath(IAndroidTarget.ANDROID_RS_CLANG));
+        command[index++] = quote(getBuildToolInfo().getPath(BuildToolInfo.PathId.ANDROID_RS_CLANG));
         command[index++] = "-I";   //$NON-NLS-1$
-        command[index++] = quote(projectTarget.getPath(IAndroidTarget.ANDROID_RS));
+        command[index++] = quote(getBuildToolInfo().getPath(BuildToolInfo.PathId.ANDROID_RS));
         command[index++] = "-p";   //$NON-NLS-1$
         command[index++] = quote(genFolder.getLocation().toOSString());
         command[index++] = "-o";   //$NON-NLS-1$
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/SourceProcessor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/SourceProcessor.java
index 4ecb91d..a82d54d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/SourceProcessor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/SourceProcessor.java
@@ -17,9 +17,11 @@
 package com.android.ide.eclipse.adt.internal.build;
 
 import com.android.SdkConstants;
+import com.android.annotations.NonNull;
 import com.android.ide.eclipse.adt.internal.build.builders.BaseBuilder;
 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
 import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
+import com.android.sdklib.BuildToolInfo;
 import com.android.sdklib.IAndroidTarget;
 
 import org.eclipse.core.resources.IFile;
@@ -59,6 +61,7 @@
     private final Map<IFile, SourceFileData> mFiles = new HashMap<IFile, SourceFileData>();
 
     private final IJavaProject mJavaProject;
+    private BuildToolInfo mBuildToolInfo;
     private final IFolder mGenFolder;
     private final SourceChangeHandler mDeltaVisitor;
 
@@ -83,9 +86,11 @@
         return path;
     }
 
-    protected SourceProcessor(IJavaProject javaProject, IFolder genFolder,
-            SourceChangeHandler deltaVisitor) {
+    protected SourceProcessor(@NonNull IJavaProject javaProject,
+            @NonNull BuildToolInfo buildToolInfo, @NonNull IFolder genFolder,
+            @NonNull SourceChangeHandler deltaVisitor) {
         mJavaProject = javaProject;
+        mBuildToolInfo = buildToolInfo;
         mGenFolder = genFolder;
         mDeltaVisitor = deltaVisitor;
 
@@ -109,8 +114,13 @@
         }
     }
 
-    protected SourceProcessor(IJavaProject javaProject, IFolder genFolder) {
-        this(javaProject, genFolder, new SourceChangeHandler());
+    protected SourceProcessor(@NonNull IJavaProject javaProject,
+            @NonNull BuildToolInfo buildToolInfo, @NonNull IFolder genFolder) {
+        this(javaProject, buildToolInfo, genFolder, new SourceChangeHandler());
+    }
+
+    public void setBuildToolInfo(BuildToolInfo buildToolInfo) {
+        mBuildToolInfo = buildToolInfo;
     }
 
 
@@ -168,6 +178,10 @@
         return mJavaProject;
     }
 
+    final BuildToolInfo getBuildToolInfo() {
+        return mBuildToolInfo;
+    }
+
     final IFolder getGenFolder() {
         return mGenFolder;
     }
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/BaseBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/BaseBuilder.java
index f58af56..1cbf7f2 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/BaseBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/BaseBuilder.java
@@ -16,19 +16,24 @@
 
 package com.android.ide.eclipse.adt.internal.build.builders;
 
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
 import com.android.ide.common.sdk.LoadStatus;
 import com.android.ide.eclipse.adt.AdtConstants;
 import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.internal.build.BuildHelper;
 import com.android.ide.eclipse.adt.internal.build.Messages;
+import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity;
 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
 import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
 import com.android.ide.eclipse.adt.internal.project.XmlErrorHandler;
 import com.android.ide.eclipse.adt.internal.project.XmlErrorHandler.XmlErrorListener;
+import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
 import com.android.ide.eclipse.adt.io.IFileWrapper;
 import com.android.io.IAbstractFile;
 import com.android.io.StreamException;
+import com.android.sdklib.BuildToolInfo;
 import com.android.sdklib.IAndroidTarget;
 
 import org.eclipse.core.resources.IContainer;
@@ -64,6 +69,13 @@
     private SAXParserFactory mParserFactory;
 
     /**
+     * The build tool to use to build. This is guaranteed to be non null after a call to
+     * {@link #abortOnBadSetup(IJavaProject, ProjectState)} since this will throw if it can't be
+     * queried.
+     */
+    protected BuildToolInfo mBuildToolInfo;
+
+    /**
      * Base Resource Delta Visitor to handle XML error
      */
     protected static class BaseDeltaVisitor implements XmlErrorListener {
@@ -293,9 +305,11 @@
      * display any errors.
      *
      * @param javaProject The {@link IJavaProject} being compiled.
+     * @param projectState the project state, optional. will be queried if null.
      * @throws CoreException
      */
-    protected void abortOnBadSetup(IJavaProject javaProject) throws AbortBuildException {
+    protected void abortOnBadSetup(@NonNull IJavaProject javaProject,
+            @Nullable ProjectState projectState) throws AbortBuildException, CoreException {
         IProject iProject = javaProject.getProject();
         // check if we have finished loading the project target.
         Sdk sdk = Sdk.getCurrent();
@@ -303,8 +317,12 @@
             throw new AbortBuildException();
         }
 
+        if (projectState == null) {
+            projectState = Sdk.getProjectState(javaProject.getProject());
+        }
+
         // get the target for the project
-        IAndroidTarget target = sdk.getTarget(javaProject.getProject());
+        IAndroidTarget target = projectState.getTarget();
 
         if (target == null) {
             throw new AbortBuildException();
@@ -315,6 +333,20 @@
             throw new AbortBuildException();
        }
 
+        mBuildToolInfo = projectState.getBuildToolInfo();
+        if (mBuildToolInfo == null) {
+            mBuildToolInfo = sdk.getLatestBuildTool();
+
+            if (mBuildToolInfo == null) {
+                throw new AbortBuildException();
+            } else {
+                AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, iProject,
+                        String.format("Using default Build Tools revision %s",
+                                mBuildToolInfo.getRevision())
+                        );
+            }
+        }
+
         // abort if there are TARGET or ADT type markers
         stopOnMarker(iProject, AdtConstants.MARKER_TARGET, IResource.DEPTH_ZERO,
                 false /*checkSeverity*/);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java
index 6e3dce3..baeb154 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java
@@ -341,7 +341,7 @@
 
             // Top level check to make sure the build can move forward. Only do this after recording
             // delta changes.
-            abortOnBadSetup(javaProject);
+            abortOnBadSetup(javaProject, projectState);
 
             // Get the output stream. Since the builder is created for the life of the
             // project, they can be kept around.
@@ -369,7 +369,7 @@
                 if (DEBUG_LOG) {
                     AdtPlugin.log(IStatus.INFO, "%s running crunch!", project.getName());
                 }
-                BuildHelper helper = new BuildHelper(project,
+                BuildHelper helper = new BuildHelper(project, mBuildToolInfo,
                         mOutStream, mErrStream,
                         false /*jumbo mode doesn't matter here*/,
                         false /*dex merger doesn't matter here*/,
@@ -484,7 +484,7 @@
                         AdtConstants.DEX_OPTIONS_DISABLE_MERGER);
                 Boolean dexMerger = Boolean.valueOf(dexMergerStr);
 
-                BuildHelper helper = new BuildHelper(project,
+                BuildHelper helper = new BuildHelper(project, mBuildToolInfo,
                         mOutStream, mErrStream,
                         jumbo.booleanValue(),
                         dexMerger.booleanValue(),
@@ -913,8 +913,9 @@
     }
 
     @Override
-    protected void abortOnBadSetup(IJavaProject javaProject) throws AbortBuildException {
-        super.abortOnBadSetup(javaProject);
+    protected void abortOnBadSetup(IJavaProject javaProject, ProjectState projectState)
+            throws AbortBuildException, CoreException {
+        super.abortOnBadSetup(javaProject, projectState);
 
         IProject iProject = getProject();
 
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java
index 0f0cbcc..ae6b3f5 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java
@@ -47,6 +47,7 @@
 import com.android.manifmerger.ManifestMerger;
 import com.android.manifmerger.MergerLog;
 import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.BuildToolInfo;
 import com.android.sdklib.IAndroidTarget;
 import com.android.sdklib.internal.build.BuildConfigGenerator;
 import com.android.sdklib.internal.build.SymbolLoader;
@@ -95,7 +96,6 @@
  * </ul>
  *
  */
-@SuppressWarnings("deprecation")
 public class PreCompilerBuilder extends BaseBuilder {
 
     /** This ID is used in plugin.xml and in each project's .project file.
@@ -144,6 +144,7 @@
      */
     private DerivedProgressMonitor mDerivedProgressMonitor;
 
+    private AidlProcessor mAidlProcessor;
     private RenderScriptProcessor mRenderScriptProcessor;
 
     /**
@@ -276,7 +277,9 @@
             IJavaProject javaProject = JavaCore.create(project);
 
             // Top level check to make sure the build can move forward.
-            abortOnBadSetup(javaProject);
+            abortOnBadSetup(javaProject, projectState);
+
+            setupSourceProcessors(javaProject, projectState);
 
             // now we need to get the classpath list
             List<IPath> sourceFolderPathList = BaseProjectHelper.getSourceClasspaths(javaProject);
@@ -707,8 +710,8 @@
                     proguardFile = androidOutputFolder.getFile(AdtConstants.FN_AAPT_PROGUARD);
                 }
 
-                handleResources(project, javaPackage, projectTarget, manifestFile, libProjects,
-                        isLibrary, proguardFile);
+                handleResources(project, javaPackage, projectTarget, manifestFile,
+                        libProjects, isLibrary, proguardFile);
             }
 
             if (processorStatus == SourceProcessor.COMPILE_STATUS_NONE &&
@@ -796,18 +799,25 @@
                 mLastBuildConfigMode = v;
             }
 
-            IJavaProject javaProject = JavaCore.create(project);
-
-            // load the source processors
-            SourceProcessor aidlProcessor = new AidlProcessor(javaProject, mGenFolder);
-            mRenderScriptProcessor = new RenderScriptProcessor(javaProject, mGenFolder);
-            mProcessors.add(aidlProcessor);
-            mProcessors.add(mRenderScriptProcessor);
         } catch (Throwable throwable) {
             AdtPlugin.log(throwable, "Failed to finish PrecompilerBuilder#startupOnInitialize()");
         }
     }
 
+    private void setupSourceProcessors(@NonNull IJavaProject javaProject,
+            @NonNull ProjectState projectState) {
+        if (mAidlProcessor == null) {
+            mAidlProcessor = new AidlProcessor(javaProject, mBuildToolInfo, mGenFolder);
+            mRenderScriptProcessor = new RenderScriptProcessor(javaProject, mBuildToolInfo,
+                    mGenFolder);
+            mProcessors.add(mAidlProcessor);
+            mProcessors.add(mRenderScriptProcessor);
+        } else {
+            mAidlProcessor.setBuildToolInfo(mBuildToolInfo);
+            mRenderScriptProcessor.setBuildToolInfo(mBuildToolInfo);
+        }
+    }
+
     @SuppressWarnings("deprecation")
     private void handleBuildConfig(@SuppressWarnings("rawtypes") Map args)
             throws IOException, CoreException {
@@ -916,7 +926,7 @@
                     outFile.getLocation().toFile(),
                     manifest.getLocation().toFile(),
                     libManifests,
-                    null /*injectAttributes*/) == false) {
+                    null /*injectAttributes*/, null /*packageOverride*/) == false) {
                 if (errors.size() > 1) {
                     StringBuilder sb = new StringBuilder();
                     for (String s : errors) {
@@ -1054,7 +1064,7 @@
         // launch aapt: create the command line
         ArrayList<String> array = new ArrayList<String>();
 
-        String aaptPath = projectTarget.getPath(IAndroidTarget.AAPT);
+        String aaptPath = mBuildToolInfo.getPath(BuildToolInfo.PathId.AAPT);
 
         array.add(aaptPath);
         array.add("package"); //$NON-NLS-1$
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/ResourceManagerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/ResourceManagerBuilder.java
index 39f7d1f..770710d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/ResourceManagerBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/ResourceManagerBuilder.java
@@ -24,8 +24,6 @@
 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity;
 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
 import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
-import com.android.ide.eclipse.adt.internal.sdk.Sdk;
-import com.android.sdklib.IAndroidTarget;
 import com.android.utils.Pair;
 
 import org.eclipse.core.resources.IFolder;
@@ -87,7 +85,7 @@
         // check for existing target marker, in which case we abort.
         // (this means: no SDK, no target, or unresolvable target.)
         try {
-            abortOnBadSetup(javaProject);
+            abortOnBadSetup(javaProject, null);
         } catch (AbortBuildException e) {
             return null;
         }
@@ -129,13 +127,6 @@
             return null;
         }
 
-        // check the project has a target
-        IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project);
-        if (projectTarget == null) {
-            // no target. marker has been set by the container initializer: exit silently.
-            return null;
-        }
-
         // check the 'gen' source folder is present
         boolean hasGenSrcFolder = false; // whether the project has a 'gen' source folder setup
 
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java
index f3d6371..c526edf 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java
@@ -586,7 +586,8 @@
             // project that have been resolved before the sdk was loaded
             // will have a ProjectState where the IAndroidTarget is null
             // so we load the target now that the SDK is loaded.
-            IAndroidTarget target = currentSdk.loadTarget(Sdk.getProjectState(iProject));
+            IAndroidTarget target = currentSdk.loadTargetAndBuildTools(
+                    Sdk.getProjectState(iProject));
             if (target == null) {
                 // this is really not supposed to happen. This would mean there are cached paths,
                 // but project.properties was deleted. Keep the project in the list to force
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java
index d215f2f..52870a4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java
@@ -31,6 +31,7 @@
 import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
 import com.android.ide.eclipse.adt.io.IFileWrapper;
+import com.android.sdklib.BuildToolInfo;
 import com.android.sdklib.build.ApkCreationException;
 import com.android.sdklib.build.DuplicateFileException;
 import com.android.sdklib.internal.project.ProjectProperties;
@@ -132,7 +133,17 @@
             String dexMergerStr = projectState.getProperty(AdtConstants.DEX_OPTIONS_DISABLE_MERGER);
             Boolean dexMerger = Boolean.valueOf(dexMergerStr);
 
-            BuildHelper helper = new BuildHelper(project,
+            BuildToolInfo buildToolInfo = projectState.getBuildToolInfo();
+            if (buildToolInfo == null) {
+                buildToolInfo = Sdk.getCurrent().getLatestBuildTool();
+            }
+
+            if (buildToolInfo == null) {
+                throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
+                        "No Build Tools installed in the SDK."));
+            }
+
+            BuildHelper helper = new BuildHelper(project, buildToolInfo,
                     fakeStream, fakeStream,
                     jumbo.booleanValue(),
                     dexMerger.booleanValue(),
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/ProjectState.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/ProjectState.java
index 64053ad..4628509 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/ProjectState.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/ProjectState.java
@@ -19,6 +19,7 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.sdklib.BuildToolInfo;
 import com.android.sdklib.IAndroidTarget;
 import com.android.sdklib.internal.project.ProjectProperties;
 import com.android.sdklib.internal.project.ProjectPropertiesWorkingCopy;
@@ -160,6 +161,7 @@
     private final IProject mProject;
     private final ProjectProperties mProperties;
     private IAndroidTarget mTarget;
+    private BuildToolInfo mBuildToolInfo;
 
     /**
      * list of libraries. Access to this list must be protected by
@@ -240,6 +242,23 @@
         return mTarget;
     }
 
+    public void setBuildToolInfo(BuildToolInfo buildToolInfo) {
+        mBuildToolInfo = buildToolInfo;
+    }
+
+    public BuildToolInfo getBuildToolInfo() {
+        return mBuildToolInfo;
+    }
+
+    /**
+     * Returns the build tools version from the project's properties.
+     * @return the value or null
+     */
+    @Nullable
+    public String getBuildToolInfoVersion() {
+        return mProperties.getProperty(ProjectProperties.PROPERTY_BUILD_TOOLS);
+    }
+
     public static class LibraryDifference {
         public boolean removed = false;
         public boolean added = false;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
index 16df952..74f08c2 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
@@ -43,6 +43,7 @@
 import com.android.io.StreamException;
 import com.android.prefs.AndroidLocation.AndroidLocationException;
 import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.BuildToolInfo;
 import com.android.sdklib.IAndroidTarget;
 import com.android.sdklib.SdkManager;
 import com.android.sdklib.devices.DeviceManager;
@@ -50,10 +51,13 @@
 import com.android.sdklib.internal.project.ProjectProperties;
 import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
 import com.android.sdklib.internal.project.ProjectPropertiesWorkingCopy;
+import com.android.sdklib.repository.FullRevision;
 import com.android.utils.ILogger;
+import com.google.common.collect.Maps;
 
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IMarker;
 import org.eclipse.core.resources.IMarkerDelta;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
@@ -130,7 +134,7 @@
     }
 
     private final SdkManager mManager;
-    private final DexWrapper mDexWrapper;
+    private final Map<String, DexWrapper> mDexWrappers = Maps.newHashMap();
     private final AvdManager mAvdManager;
     private final DeviceManager mDeviceManager;
 
@@ -265,17 +269,6 @@
             // get an SdkManager object for the location
             SdkManager manager = SdkManager.createManager(sdkLocation, log);
             if (manager != null) {
-                // load DX.
-                DexWrapper dexWrapper = new DexWrapper();
-                String dexLocation =
-                        sdkLocation + File.separator +
-                        SdkConstants.OS_SDK_PLATFORM_TOOLS_LIB_FOLDER + SdkConstants.FN_DX_JAR;
-                IStatus res = dexWrapper.loadDex(dexLocation);
-                if (res != Status.OK_STATUS) {
-                    log.error(null, res.getMessage());
-                    dexWrapper = null;
-                }
-
                 // create the AVD Manager
                 AvdManager avdManager = null;
                 try {
@@ -283,7 +276,7 @@
                 } catch (AndroidLocationException e) {
                     log.error(e, "Error parsing the AVDs");
                 }
-                sCurrentSdk = new Sdk(manager, dexWrapper, avdManager);
+                sCurrentSdk = new Sdk(manager, avdManager);
                 return sCurrentSdk;
             } else {
                 StringBuilder sb = new StringBuilder("Error Loading the SDK:\n");
@@ -376,6 +369,24 @@
         return mManager.getTargetFromHashString(hash);
     }
 
+    @Nullable
+    public BuildToolInfo getBuildToolInfo(@Nullable String buildToolVersion) {
+        if (buildToolVersion != null) {
+            try {
+                return mManager.getBuildTool(FullRevision.parseRevision(buildToolVersion));
+            } catch (Exception e) {
+                // ignore, return null below.
+            }
+        }
+
+        return null;
+    }
+
+    @Nullable
+    public BuildToolInfo getLatestBuildTool() {
+        return mManager.getLatestBuildTool(false /*isPreview*/);
+    }
+
     /**
      * Initializes a new project with a target. This creates the <code>project.properties</code>
      * file.
@@ -487,7 +498,7 @@
 
                 // try to resolve the target
                 if (AdtPlugin.getDefault().getSdkLoadStatus() == LoadStatus.LOADED) {
-                    sCurrentSdk.loadTarget(state);
+                    sCurrentSdk.loadTargetAndBuildTools(state);
                 }
             }
 
@@ -513,26 +524,84 @@
     }
 
     /**
-     * Loads the {@link IAndroidTarget} for a given project.
+     * Loads the {@link IAndroidTarget} and BuildTools for a given project.
      * <p/>This method will get the target hash string from the project properties, and resolve
      * it to an {@link IAndroidTarget} object and store it inside the {@link ProjectState}.
      * @param state the state representing the project to load.
      * @return the target that was loaded.
      */
     @Nullable
-    public IAndroidTarget loadTarget(ProjectState state) {
+    public IAndroidTarget loadTargetAndBuildTools(ProjectState state) {
         IAndroidTarget target = null;
         if (state != null) {
             String hash = state.getTargetHashString();
             if (hash != null) {
                 state.setTarget(target = getTargetFromHashString(hash));
             }
+
+            String markerMessage = null;
+            String buildToolInfoVersion = state.getBuildToolInfoVersion();
+            if (buildToolInfoVersion != null) {
+                BuildToolInfo buildToolsInfo = getBuildToolInfo(buildToolInfoVersion);
+
+                if (buildToolsInfo != null) {
+                    state.setBuildToolInfo(buildToolsInfo);
+                } else {
+                    markerMessage = String.format("Unable to resolve %s property value '%s'",
+                                        ProjectProperties.PROPERTY_BUILD_TOOLS,
+                                        buildToolInfoVersion);
+                }
+            } else {
+                // this is ok, we'll use the latest one automatically.
+                state.setBuildToolInfo(null);
+            }
+
+            handleBuildToolsMarker(state.getProject(), markerMessage);
         }
 
         return target;
     }
 
     /**
+     * Adds or edit a build tools marker from the given project. This is done through a Job.
+     * @param project the project
+     * @param markerMessage the message. if null the marker is removed.
+     */
+    private void handleBuildToolsMarker(final IProject project, final String markerMessage) {
+        Job markerJob = new Job("Android SDK: Build Tools Marker") {
+            @Override
+            protected IStatus run(IProgressMonitor monitor) {
+                try {
+                    if (project.isAccessible()) {
+                        // always delete existing marker first
+                        project.deleteMarkers(AdtConstants.MARKER_BUILD_TOOLS, true,
+                                IResource.DEPTH_ZERO);
+
+                        // add the new one if needed.
+                        if (markerMessage != null) {
+                            BaseProjectHelper.markProject(project,
+                                    AdtConstants.MARKER_BUILD_TOOLS,
+                                    markerMessage, IMarker.SEVERITY_ERROR,
+                                    IMarker.PRIORITY_HIGH);
+                        }
+                    }
+                } catch (CoreException e2) {
+                    AdtPlugin.log(e2, null);
+                    // Don't return e2.getStatus(); the job control will then produce
+                    // a popup with this error, which isn't very interesting for the
+                    // user.
+                }
+
+                return Status.OK_STATUS;
+            }
+        };
+
+        // build jobs are run after other interactive jobs
+        markerJob.setPriority(Job.BUILD);
+        markerJob.schedule();
+    }
+
+    /**
      * Checks and loads (if needed) the data for a given target.
      * <p/> The data is loaded in a separate {@link Job}, and opened editors will be notified
      * through their implementation of {@link ITargetChangeListener#onTargetLoaded(IAndroidTarget)}.
@@ -668,8 +737,38 @@
      * Returns a {@link DexWrapper} object to be used to execute dx commands. If dx.jar was not
      * loaded properly, then this will return <code>null</code>.
      */
-    public DexWrapper getDexWrapper() {
-        return mDexWrapper;
+    @Nullable
+    public DexWrapper getDexWrapper(@Nullable BuildToolInfo buildToolInfo) {
+        if (buildToolInfo == null) {
+            return null;
+        }
+        synchronized (LOCK) {
+            String dexLocation = buildToolInfo.getPath(BuildToolInfo.PathId.DX_JAR);
+            DexWrapper dexWrapper = mDexWrappers.get(dexLocation);
+
+            if (dexWrapper == null) {
+                // load DX.
+                dexWrapper = new DexWrapper();
+                IStatus res = dexWrapper.loadDex(dexLocation);
+                if (res != Status.OK_STATUS) {
+                    AdtPlugin.log(null, res.getMessage());
+                    dexWrapper = null;
+                } else {
+                    mDexWrappers.put(dexLocation, dexWrapper);
+                }
+            }
+
+            return dexWrapper;
+        }
+    }
+
+    public void unloadDexWrappers() {
+        synchronized (LOCK) {
+            for (DexWrapper wrapper : mDexWrappers.values()) {
+                wrapper.unload();
+            }
+            mDexWrappers.clear();
+        }
     }
 
     /**
@@ -763,9 +862,8 @@
         }
     }
 
-    private Sdk(SdkManager manager, DexWrapper dexWrapper, AvdManager avdManager) {
+    private Sdk(SdkManager manager, AvdManager avdManager) {
         mManager = manager;
-        mDexWrapper = dexWrapper;
         mAvdManager = avdManager;
 
         // listen to projects closing
@@ -787,8 +885,7 @@
         // update whatever ProjectState is already present with new IAndroidTarget objects.
         synchronized (LOCK) {
             for (Entry<IProject, ProjectState> entry: sProjectStateMap.entrySet()) {
-                entry.getValue().setTarget(
-                        getTargetFromHashString(entry.getValue().getTargetHashString()));
+                loadTargetAndBuildTools(entry.getValue());
             }
         }
     }
@@ -1042,7 +1139,7 @@
 
                     ProjectState state = Sdk.getProjectState(iProject);
 
-                    // get the current target
+                    // get the current target and build tools
                     IAndroidTarget oldTarget = state.getTarget();
 
                     // get the current library flag
@@ -1051,7 +1148,7 @@
                     LibraryDifference diff = state.reloadProperties();
 
                     // load the (possibly new) target.
-                    IAndroidTarget newTarget = loadTarget(state);
+                    IAndroidTarget newTarget = loadTargetAndBuildTools(state);
 
                     // reload the libraries if needed
                     if (diff.hasDiff()) {