OSDN Git Service

Prevent project from building before their target data has been loaded.
authorXavier Ducrohet <xav@android.com>
Wed, 16 Dec 2009 01:10:13 +0000 (17:10 -0800)
committerXavier Ducrohet <xav@android.com>
Wed, 16 Dec 2009 23:44:23 +0000 (15:44 -0800)
Projects that attempts to build before their target data has been loaded
are put in a list of project to be recompile upon load completion.

Bug: 2303254
Change-Id: Iad9a652ac5455432b2616334bcc8536e169c2adb

eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BaseBuilder.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/PreCompilerBuilder.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ResourceManagerBuilder.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java

index 854d1b1..590631f 100644 (file)
@@ -1015,7 +1015,7 @@ public class AdtPlugin extends AbstractUIPlugin {
                     final List<ITargetChangeListener> listeners =
                         (List<ITargetChangeListener>)mTargetChangeListeners.clone();
                     final SubMonitor progress2 = progress;
-                    AdtPlugin.getDisplay().syncExec(new Runnable() {
+                    AdtPlugin.getDisplay().asyncExec(new Runnable() {
                         public void run() {
                             for (ITargetChangeListener listener : listeners) {
                                 try {
index c58b032..a7c0e62 100644 (file)
@@ -264,8 +264,10 @@ public class ApkBuilder extends BaseBuilder {
         IProject[] referencedProjects = null;
 
         try {
+            IJavaProject javaProject = JavaCore.create(project);
+
             // Top level check to make sure the build can move forward.
-            abortOnBadSetup(project);
+            abortOnBadSetup(javaProject);
 
             // get the list of referenced projects.
             referencedProjects = ProjectHelper.getReferencedProjects(project);
@@ -273,7 +275,6 @@ public class ApkBuilder extends BaseBuilder {
 
             // get the output folder, this method returns the path with a trailing
             // separator
-            IJavaProject javaProject = JavaCore.create(project);
             IFolder outputFolder = BaseProjectHelper.getOutputFolder(project);
 
             // now we need to get the classpath list
@@ -1298,12 +1299,14 @@ public class ApkBuilder extends BaseBuilder {
     }
 
     @Override
-    protected void abortOnBadSetup(IProject project) throws CoreException {
-        super.abortOnBadSetup(project);
-
-        // for this version, we stop on any marker (ie also markers coming from JDT)
-        IMarker[] markers = project.findMarkers(null /*type*/, false /*includeSubtypes*/,
-                IResource.DEPTH_ZERO);
+    protected void abortOnBadSetup(IJavaProject javaProject) throws CoreException {
+        super.abortOnBadSetup(javaProject);
+
+        // for this version, we stop on any marker (ie also markers coming from JDT).
+        // The depth is set to ZERO to make sure we don't stop on warning on resources.
+        // Only markers set directly on the project are considered.
+        IMarker[] markers = javaProject.getProject().findMarkers(null /*type*/,
+                false /*includeSubtypes*/, IResource.DEPTH_ZERO);
 
         if (markers.length > 0) {
             stopBuild("");
index cc82efb..fde293b 100644 (file)
@@ -24,6 +24,8 @@ 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.LoadStatus;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdklib.IAndroidTarget;
 
 import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.resources.IFile;
@@ -880,25 +882,41 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
      * Aborts the build if the SDK/project setups are broken. This does not
      * display any errors.
      *
-     * @param project The {@link IJavaProject} being compiled.
+     * @param javaProject The {@link IJavaProject} being compiled.
      * @throws CoreException
      */
-    protected void abortOnBadSetup(IProject project) throws CoreException {
-        // check if we have finished loading the SDK.
-        if (AdtPlugin.getDefault().getSdkLoadStatus() != LoadStatus.LOADED) {
+    protected void abortOnBadSetup(IJavaProject javaProject) throws CoreException {
+        IProject iProject = javaProject.getProject();
+        // check if we have finished loading the project target.
+        Sdk sdk = Sdk.getCurrent();
+        if (sdk == null) {
             // we exit silently
             stopBuild("SDK is not loaded yet");
         }
 
+        // get the target for the project
+        IAndroidTarget target = sdk.getTarget(javaProject.getProject());
+
+        if (target == null) {
+            // we exit silently
+            stopBuild("Project target not resolved yet.");
+        }
+
+        // check on the target data.
+        if (sdk.checkAndLoadTargetData(target, javaProject) != LoadStatus.LOADED) {
+            // we exit silently
+            stopBuild("Project target not loaded yet.");
+       }
+
         // abort if there are TARGET or ADT type markers
-        IMarker[] markers = project.findMarkers(AndroidConstants.MARKER_TARGET,
+        IMarker[] markers = iProject.findMarkers(AndroidConstants.MARKER_TARGET,
                 false /*includeSubtypes*/, IResource.DEPTH_ZERO);
 
         if (markers.length > 0) {
             stopBuild("");
         }
 
-        markers = project.findMarkers(AndroidConstants.MARKER_ADT, false /*includeSubtypes*/,
+        markers = iProject.findMarkers(AndroidConstants.MARKER_ADT, false /*includeSubtypes*/,
                 IResource.DEPTH_ZERO);
 
         if (markers.length > 0) {
index 9ca7842..da73406 100644 (file)
@@ -215,11 +215,11 @@ public class PreCompilerBuilder extends BaseBuilder {
 
             // get the project objects
             IProject project = getProject();
+            IJavaProject javaProject = JavaCore.create(project);
 
             // Top level check to make sure the build can move forward.
-            abortOnBadSetup(project);
+            abortOnBadSetup(javaProject);
 
-            IJavaProject javaProject = JavaCore.create(project);
             IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project);
 
             // now we need to get the classpath list
index b6e6ac3..1aeb19f 100644 (file)
@@ -73,13 +73,14 @@ public class ResourceManagerBuilder extends BaseBuilder {
             throws CoreException {
         // Get the project.
         IProject project = getProject();
+        IJavaProject javaProject = JavaCore.create(project);
 
         // Clear the project of the generic markers
         removeMarkersFromProject(project, AndroidConstants.MARKER_ADT);
 
         // check for existing target marker, in which case we abort.
         // (this means: no SDK, no target, or unresolvable target.)
-        abortOnBadSetup(project);
+        abortOnBadSetup(javaProject);
 
         // Check the compiler compliance level, displaying the error message
         // since this is the first builder.
@@ -124,7 +125,6 @@ public class ResourceManagerBuilder extends BaseBuilder {
 
         // check the 'gen' source folder is present
         boolean hasGenSrcFolder = false; // whether the project has a 'gen' source folder setup
-        IJavaProject javaProject = JavaCore.create(project);
 
         IClasspathEntry[] classpaths = javaProject.readRawClasspath();
         if (classpaths != null) {
index 1634450..11fe237 100644 (file)
@@ -172,7 +172,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
                 // if we are loaded and the target is non null, we create a valid ClassPathContainer
                 if (sdkIsLoaded && target != null) {
                     // first make sure the target has loaded its data
-                    Sdk.getCurrent().checkAndLoadTargetData(target);
+                    Sdk.getCurrent().checkAndLoadTargetData(target, null /*project*/);
 
                     String targetName = target.getClasspathName();
 
index 3fcd2a9..39a0a79 100644 (file)
@@ -56,6 +56,7 @@ import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 
 /**
@@ -73,13 +74,23 @@ public class Sdk implements IProjectListener, IFileListener {
     private final SdkManager mManager;
     private final AvdManager mAvdManager;
 
+    /**
+     * Data bundled using during the load of Target data.
+     * <p/>This contains the {@link LoadStatus} and a list of projects that attempted
+     * to compile before the loading was finished. Those projects will be recompiled
+     * at the end of the loading.
+     */
+    private final static class TargetLoadBundle {
+        LoadStatus status;
+        final HashSet<IJavaProject> projecsToReload = new HashSet<IJavaProject>();
+    }
+
     /** Map associating an {@link IAndroidTarget} to an {@link AndroidTargetData} */
     private final HashMap<IAndroidTarget, AndroidTargetData> mTargetDataMap =
         new HashMap<IAndroidTarget, AndroidTargetData>();
-    /** Map associating an {@link IAndroidTarget} and the {@link LoadStatus} of its
-     * {@link AndroidTargetData}. */
-    private final HashMap<IAndroidTarget, LoadStatus> mTargetDataStatusMap =
-        new HashMap<IAndroidTarget, LoadStatus>();
+    /** Map associating an {@link IAndroidTarget} and its {@link TargetLoadBundle}. */
+    private final HashMap<IAndroidTarget, TargetLoadBundle> mTargetDataStatusMap =
+        new HashMap<IAndroidTarget, TargetLoadBundle>();
 
     /** Map associating {@link IProject} and their resolved {@link IAndroidTarget}. */
     private final HashMap<IProject, IAndroidTarget> mProjectTargetMap =
@@ -155,7 +166,7 @@ public class Sdk implements IProjectListener, IFileListener {
      * <p/>If the SDK failed to load, it displays an error to the user.
      * @param sdkLocation the OS path to the SDK.
      */
-    public static Sdk loadSdk(String sdkLocation) {
+    public static synchronized Sdk loadSdk(String sdkLocation) {
         if (sCurrentSdk != null) {
             sCurrentSdk.dispose();
             sCurrentSdk = null;
@@ -207,7 +218,7 @@ public class Sdk implements IProjectListener, IFileListener {
     /**
      * Returns the current {@link Sdk} object.
      */
-    public static Sdk getCurrent() {
+    public static synchronized Sdk getCurrent() {
         return sCurrentSdk;
     }
 
@@ -436,18 +447,41 @@ public class Sdk implements IProjectListener, IFileListener {
      * 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)}.
+     * <p/>An optional project as second parameter can be given to be recompiled once the target
+     * data is finished loading.
+     * <p/>The return value is non-null only if the target data has already been loaded (and in this
+     * case is the status of the load operation)
      * @param target the target to load.
+     * @param project an optional project to be recompiled when the target data is loaded.
+     * If the target is already loaded, nothing happens.
+     * @return The load status if the target data is already loaded.
      */
-    public void checkAndLoadTargetData(final IAndroidTarget target) {
+    public LoadStatus checkAndLoadTargetData(final IAndroidTarget target, IJavaProject project) {
         boolean loadData = false;
 
         synchronized (AdtPlugin.getDefault().getSdkLockObject()) {
-            LoadStatus status = mTargetDataStatusMap.get(target);
-            if (status == null) {
+            TargetLoadBundle bundle = mTargetDataStatusMap.get(target);
+            if (bundle == null) {
+                bundle = new TargetLoadBundle();
+                mTargetDataStatusMap.put(target,bundle);
+
                 // set status to loading
-                mTargetDataStatusMap.put(target, LoadStatus.LOADING);
+                bundle.status = LoadStatus.LOADING;
+
+                // add project to bundle
+                if (project != null) {
+                    bundle.projecsToReload.add(project);
+                }
+
                 // and set the flag to start the loading below
                 loadData = true;
+            } else if (bundle.status == LoadStatus.LOADING) {
+                // add project to bundle
+                if (project != null) {
+                    bundle.projecsToReload.add(project);
+                }
+            } else if (bundle.status == LoadStatus.LOADED || bundle.status == LoadStatus.FAILED) {
+                return bundle.status;
             }
         }
 
@@ -455,19 +489,42 @@ public class Sdk implements IProjectListener, IFileListener {
             Job job = new Job(String.format("Loading data for %1$s", target.getFullName())) {
                 @Override
                 protected IStatus run(IProgressMonitor monitor) {
+                    AdtPlugin plugin = AdtPlugin.getDefault();
                     try {
                         IStatus status = new AndroidTargetParser(target).run(monitor);
-                        AdtPlugin plugin = AdtPlugin.getDefault();
+
+                        IJavaProject[] javaProjectArray = null;
+
                         synchronized (plugin.getSdkLockObject()) {
+                            TargetLoadBundle bundle = mTargetDataStatusMap.get(target);
+
                             if (status.getCode() != IStatus.OK) {
-                                mTargetDataStatusMap.put(target, LoadStatus.FAILED);
+                                bundle.status = LoadStatus.FAILED;
+                                bundle.projecsToReload.clear();
                             } else {
-                                mTargetDataStatusMap.put(target, LoadStatus.LOADED);
+                                bundle.status = LoadStatus.LOADED;
+
+                                // Prepare the array of project to recompile.
+                                // The call is done outside of the synchronized block.
+                                javaProjectArray = bundle.projecsToReload.toArray(
+                                        new IJavaProject[bundle.projecsToReload.size()]);
+
+                                // and update the UI of the editors that depend on the target data.
                                 plugin.updateTargetListeners(target);
                             }
                         }
+
+                        if (javaProjectArray != null) {
+                            AndroidClasspathContainerInitializer.updateProjects(javaProjectArray);
+                        }
+
                         return status;
                     } catch (Throwable t) {
+                        synchronized (plugin.getSdkLockObject()) {
+                            TargetLoadBundle bundle = mTargetDataStatusMap.get(target);
+                            bundle.status = LoadStatus.FAILED;
+                        }
+
                         AdtPlugin.log(t, "Exception in checkAndLoadTargetData.");    //$NON-NLS-1$
                         return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
                                 String.format(
@@ -480,17 +537,8 @@ public class Sdk implements IProjectListener, IFileListener {
             job.setPriority(Job.BUILD); // build jobs are run after other interactive jobs
             job.schedule();
         }
-    }
 
-    /**
-     * Returns the {@link LoadStatus} for the data of a given {@link IAndroidTarget}.
-     * @param target the target that is queried.
-     * @return the status or <code>null</code> if the data was not loaded.
-     */
-    public LoadStatus getTargetDataLoadStatus(IAndroidTarget target) {
-        synchronized (AdtPlugin.getDefault().getSdkLockObject()) {
-            return mTargetDataStatusMap.get(target);
-        }
+        return null;
     }
 
     /**