From 81c5fb5448a6342cb3bb29ea501fccf95573288c Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Fri, 2 Mar 2012 11:59:37 -0800 Subject: Automatically add dependencies to Eclipse project. add libs/*.jar from Library Projects, add Java-only projects only that referenced in Library Projects (recursively so that Java projects that reference other Java projects pull those in too). Also add jar files referenced by Java-only projects. Change-Id: Ic2b10107153e0576f5e6ba34d50bd3fef95c3fea --- .../plugins/com.android.ide.eclipse.adt/plugin.xml | 51 +++--- .../com/android/ide/eclipse/adt/AdtConstants.java | 8 +- .../eclipse/adt/internal/build/BuildHelper.java | 13 +- .../AndroidClasspathContainerInitializer.java | 73 +------- .../project/BaseClasspathContainerInitializer.java | 111 ++++++++++++ .../LibraryClasspathContainerInitializer.java | 192 ++++++++++++++++++--- 6 files changed, 327 insertions(+), 121 deletions(-) create mode 100644 eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseClasspathContainerInitializer.java (limited to 'eclipse') diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml index 0cd0354..e5e8229 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml +++ b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml @@ -57,6 +57,35 @@ + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - 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 0a1f2bb..0b619fb 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 @@ -214,10 +214,16 @@ public class AdtConstants { public final static String MARKER_ADT = AdtPlugin.PLUGIN_ID + ".adtProblem"; //$NON-NLS-1$ /** Marker for Android Target errors. - * This is not cleared on each like other markers. Instead, it's cleared + * This is not cleared on each build like other markers. Instead, it's cleared * 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 Dependency errors. + * This is not cleared on each build like other markers. Instead, it's cleared + * when a LibraryClasspathContainerInitializer has succeeded in creating a + * LibraryClasspathContainer */ + public final static String MARKER_DEPENDENCY = AdtPlugin.PLUGIN_ID + ".dependencyProblem"; //$NON-NLS-1$ + /** aapt marker error when running the compile command, only to be used * in {@link PreCompilerBuilder} */ 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 ef3a7ac..e915544 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 @@ -799,9 +799,11 @@ public class BuildHelper { String configFilter, int versionCode) throws AaptExecException, AaptResultException { IAndroidTarget target = Sdk.getCurrent().getTarget(mProject); + @SuppressWarnings("deprecation") String aapt = target.getPath(IAndroidTarget.AAPT); + // Create the command line. ArrayList commandArray = new ArrayList(); - commandArray.add(target.getPath(IAndroidTarget.AAPT)); + commandArray.add(aapt); commandArray.add(aaptCommand); if (AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE) { commandArray.add("-v"); //$NON-NLS-1$ @@ -1066,8 +1068,13 @@ public class BuildHelper { IPath path = e.getPath(); IResource resource = wsRoot.findMember(path); - // case of a jar file (which could be relative to the workspace or a full path) - if (AdtConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) { + + if (resource != null && resource.getType() == IResource.PROJECT) { + // if it's a project we should just ignore it because it's going to be added + // later when we add all the referenced projects. + + } else if (AdtConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) { + // case of a jar file (which could be relative to the workspace or a full path) if (resource != null && resource.exists() && resource.getType() == IResource.FILE) { oslibraryList.add(resource.getLocation().toOSString()); 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 b2be4a1..c2493e3 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 @@ -26,22 +26,15 @@ import com.android.sdklib.IAndroidTarget; import com.android.sdklib.IAndroidTarget.IOptionalLibrary; import com.android.sdklib.SdkConstants; -import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; -import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.jdt.core.ClasspathContainerInitializer; import org.eclipse.jdt.core.IAccessRule; import org.eclipse.jdt.core.IClasspathAttribute; import org.eclipse.jdt.core.IClasspathContainer; @@ -66,7 +59,7 @@ import java.util.regex.Pattern; * Classpath container initializer responsible for binding {@link AndroidClasspathContainer} to * {@link IProject}s. This removes the hard-coded path to the android.jar. */ -public class AndroidClasspathContainerInitializer extends ClasspathContainerInitializer { +public class AndroidClasspathContainerInitializer extends BaseClasspathContainerInitializer { public static final String NULL_API_URL = ""; //$NON-NLS-1$ @@ -276,69 +269,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit }; } } finally { - if (markerMessage != null) { - // log the error and put the marker on the project if we can. - if (outputToConsole) { - AdtPlugin.printErrorToConsole(iProject, markerMessage); - } - - try { - BaseProjectHelper.markProject(iProject, AdtConstants.MARKER_TARGET, - markerMessage, IMarker.SEVERITY_ERROR, IMarker.PRIORITY_HIGH); - } catch (CoreException e) { - // In some cases, the workspace may be locked for modification when we - // pass here. - // We schedule a new job to put the marker after. - final String fmessage = markerMessage; - Job markerJob = new Job("Android SDK: Resolving error markers") { - @Override - protected IStatus run(IProgressMonitor monitor) { - try { - BaseProjectHelper.markProject(iProject, - AdtConstants.MARKER_TARGET, - fmessage, IMarker.SEVERITY_ERROR, - IMarker.PRIORITY_HIGH); - } catch (CoreException e2) { - return e2.getStatus(); - } - - return Status.OK_STATUS; - } - }; - - // build jobs are run after other interactive jobs - markerJob.setPriority(Job.BUILD); - markerJob.schedule(); - } - } else { - // no error, remove potential MARKER_TARGETs. - try { - if (iProject.exists()) { - iProject.deleteMarkers(AdtConstants.MARKER_TARGET, true, - IResource.DEPTH_INFINITE); - } - } catch (CoreException ce) { - // In some cases, the workspace may be locked for modification when we pass - // here, so we schedule a new job to put the marker after. - Job markerJob = new Job("Android SDK: Resolving error markers") { - @Override - protected IStatus run(IProgressMonitor monitor) { - try { - iProject.deleteMarkers(AdtConstants.MARKER_TARGET, true, - IResource.DEPTH_INFINITE); - } catch (CoreException e2) { - return e2.getStatus(); - } - - return Status.OK_STATUS; - } - }; - - // build jobs are run after other interactive jobs - markerJob.setPriority(Job.BUILD); - markerJob.schedule(); - } - } + processError(iProject, markerMessage, AdtConstants.MARKER_TARGET, outputToConsole); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseClasspathContainerInitializer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseClasspathContainerInitializer.java new file mode 100644 index 0000000..403b17b --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseClasspathContainerInitializer.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.adt.internal.project; + +import com.android.ide.eclipse.adt.AdtPlugin; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jdt.core.ClasspathContainerInitializer; + +/** + * Base CPC initializer providing support to all our initializer. + * + */ +abstract class BaseClasspathContainerInitializer extends ClasspathContainerInitializer { + + + /** + * Adds an error to a project, or remove all markers if error message is null + * @param project the project to modify + * @param errorMessage the errorMessage or null to remove errors. + * @param markerType the marker type to be used. + * @param outputToConsole whether to output to the console. + */ + protected static void processError(final IProject project, final String errorMessage, + final String markerType, boolean outputToConsole) { + if (errorMessage != null) { + // log the error and put the marker on the project if we can. + if (outputToConsole) { + AdtPlugin.printErrorToConsole(project, errorMessage); + } + + try { + BaseProjectHelper.markProject(project, markerType, + errorMessage, IMarker.SEVERITY_ERROR, IMarker.PRIORITY_HIGH); + } catch (CoreException e) { + // In some cases, the workspace may be locked for modification when we + // pass here. + // We schedule a new job to put the marker after. + final String fmessage = errorMessage; + Job markerJob = new Job("Android SDK: Resolving error markers") { + @Override + protected IStatus run(IProgressMonitor monitor) { + try { + BaseProjectHelper.markProject(project, + markerType, + fmessage, IMarker.SEVERITY_ERROR, + IMarker.PRIORITY_HIGH); + } catch (CoreException e2) { + return e2.getStatus(); + } + + return Status.OK_STATUS; + } + }; + + // build jobs are run after other interactive jobs + markerJob.setPriority(Job.BUILD); + markerJob.schedule(); + } + } else { + // no error, remove existing markers. + try { + if (project.exists()) { + project.deleteMarkers(markerType, true, + IResource.DEPTH_INFINITE); + } + } catch (CoreException ce) { + // In some cases, the workspace may be locked for modification when we pass + // here, so we schedule a new job to put the marker after. + Job markerJob = new Job("Android SDK: Resolving error markers") { + @Override + protected IStatus run(IProgressMonitor monitor) { + try { + project.deleteMarkers(markerType, true, + IResource.DEPTH_INFINITE); + } catch (CoreException e2) { + return e2.getStatus(); + } + + return Status.OK_STATUS; + } + }; + + // build jobs are run after other interactive jobs + markerJob.setPriority(Job.BUILD); + markerJob.schedule(); + } + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/LibraryClasspathContainerInitializer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/LibraryClasspathContainerInitializer.java index d012bc8..f21eb1f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/LibraryClasspathContainerInitializer.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/LibraryClasspathContainerInitializer.java @@ -18,20 +18,24 @@ package com.android.ide.eclipse.adt.internal.project; import com.android.ide.eclipse.adt.AdtConstants; import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.AndroidPrintStream; import com.android.ide.eclipse.adt.internal.sdk.ProjectState; import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.sdklib.SdkConstants; +import com.android.sdklib.build.JarListSanitizer; +import com.android.sdklib.build.JarListSanitizer.DifferentLibException; +import com.android.sdklib.build.JarListSanitizer.Sha1Exception; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; -import org.eclipse.jdt.core.ClasspathContainerInitializer; import org.eclipse.jdt.core.IClasspathContainer; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; @@ -40,11 +44,11 @@ import org.eclipse.jdt.core.JavaModelException; import java.io.File; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Set; -public class LibraryClasspathContainerInitializer extends ClasspathContainerInitializer { +public class LibraryClasspathContainerInitializer extends BaseClasspathContainerInitializer { public LibraryClasspathContainerInitializer() { } @@ -160,6 +164,9 @@ public class LibraryClasspathContainerInitializer extends ClasspathContainerInit IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); + // list of java project dependencies and jar files that will be built while + // going through the library projects. + Set jarFiles = new HashSet(); HashSet refProjects = new HashSet(); List libProjects = state.getFullLibraryProjects(); @@ -192,24 +199,11 @@ public class LibraryClasspathContainerInitializer extends ClasspathContainerInit } // get project dependencies - try { - IProject[] refs = libProject.getReferencedProjects(); - refProjects.addAll(Arrays.asList(refs)); - } catch (CoreException e) { - } + processReferencedProjects(libProject, refProjects, jarFiles, true); } - for (IProject p : refProjects) { - // ignore if it's an Android project, or if it's not a Java Project - try { - if (p.hasNature(JavaCore.NATURE_ID) && - p.hasNature(AdtConstants.NATURE_DEFAULT) == false) { - entries.add(JavaCore.newProjectEntry(p.getFullPath())); - } - } catch (CoreException e) { - // can't get the nature? ignore the project. - } - } + // now get the jar files for the referenced project for this project. + processReferencedProjects(iProject, refProjects, jarFiles, false); // annotations support for older version of android if (state.getTarget() != null && state.getTarget().getVersion().getApiLevel() <= 15) { @@ -217,14 +211,53 @@ public class LibraryClasspathContainerInitializer extends ClasspathContainerInit SdkConstants.FD_TOOLS + File.separator + SdkConstants.FD_SUPPORT + File.separator + SdkConstants.FN_ANNOTATIONS_JAR); - IClasspathEntry entry = JavaCore.newLibraryEntry( - new Path(annotationsJar.getAbsolutePath()), - null, // source attachment path - null); // default source attachment root path. + jarFiles.add(annotationsJar); + } - entries.add(0, entry); + // now add a classpath entry for each Java project (this is a set so dups are already + // removed) + for (IProject p : refProjects) { + entries.add(JavaCore.newProjectEntry(p.getFullPath())); } + // and process the jar files list, but first sanitize it to remove dups. + JarListSanitizer sanitizer = new JarListSanitizer( + iProject.getFolder(SdkConstants.FD_OUTPUT).getLocation().toFile(), + new AndroidPrintStream(iProject, null /*prefix*/, + AdtPlugin.getOutStream())); + + String errorMessage = null; + + try { + List sanitizedList = sanitizer.sanitize(jarFiles); + + for (File jarFile : sanitizedList) { + if (jarFile instanceof CPEFile) { + CPEFile cpeFile = (CPEFile) jarFile; + IClasspathEntry e = cpeFile.getClasspathEntry(); + + entries.add(JavaCore.newLibraryEntry( + e.getPath(), + e.getSourceAttachmentPath(), + e.getSourceAttachmentRootPath(), + e.getAccessRules(), + e.getExtraAttributes(), + e.isExported())); + } else { + entries.add(JavaCore.newLibraryEntry(new Path(jarFile.getAbsolutePath()), + null /*sourceAttachmentPath*/, null /*sourceAttachmentRootPath*/)); + } + } + } catch (DifferentLibException e) { + errorMessage = e.getMessage(); + AdtPlugin.printErrorToConsole(iProject, (Object[]) e.getDetails()); + } catch (Sha1Exception e) { + errorMessage = e.getMessage(); + } + + processError(iProject, errorMessage, AdtConstants.MARKER_DEPENDENCY, + true /*outputToConsole*/); + return new AndroidClasspathContainer( entries.toArray(new IClasspathEntry[entries.size()]), new Path(AdtConstants.CONTAINER_LIBRARIES), @@ -232,4 +265,115 @@ public class LibraryClasspathContainerInitializer extends ClasspathContainerInit IClasspathContainer.K_APPLICATION); } + + private static void processReferencedProjects(IProject project, + Set projects, Set jarFiles, boolean includeAndroidContainer) { + // get the jar dependencies of the project in the list + getJarDependencies(project, jarFiles, includeAndroidContainer); + + try { + IProject[] refs = project.getReferencedProjects(); + for (IProject p : refs) { + // ignore if it's an Android project, or if it's not a Java Project + if (p.hasNature(JavaCore.NATURE_ID) && + p.hasNature(AdtConstants.NATURE_DEFAULT) == false) { + // add this project to the list + projects.add(p); + + // and then process this project too. + processReferencedProjects(p, projects, jarFiles, includeAndroidContainer); + } + } + } catch (CoreException e) { + // can't get the referenced projects? ignore + } + } + + private static void getJarDependencies(IProject p, Set jarFiles, + boolean includeAndroidContainer) { + IJavaProject javaProject = JavaCore.create(p); + IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot(); + + // we could use IJavaProject.getResolvedClasspath directly, but we actually + // want to see the containers themselves. + IClasspathEntry[] classpaths = javaProject.readRawClasspath(); + if (classpaths != null) { + for (IClasspathEntry e : classpaths) { + // if this is a classpath variable reference, we resolve it. + if (e.getEntryKind() == IClasspathEntry.CPE_VARIABLE) { + e = JavaCore.getResolvedClasspathEntry(e); + } + + if (e.getEntryKind() == IClasspathEntry.CPE_LIBRARY) { + handleClasspathEntry(e, wsRoot, jarFiles); + } else if (e.getEntryKind() == IClasspathEntry.CPE_CONTAINER && + (includeAndroidContainer || + e.getPath().toString().startsWith( + "com.android.ide.eclipse.adt.") == false)) { + // get the container. + try { + IClasspathContainer container = JavaCore.getClasspathContainer( + e.getPath(), javaProject); + // ignore the system and default_system types as they represent + // libraries that are part of the runtime. + if (container != null && + container.getKind() == IClasspathContainer.K_APPLICATION) { + IClasspathEntry[] entries = container.getClasspathEntries(); + for (IClasspathEntry entry : entries) { + handleClasspathEntry(entry, wsRoot, jarFiles); + } + } + } catch (JavaModelException jme) { + // can't resolve the container? ignore it. + AdtPlugin.log(jme, "Failed to resolve ClasspathContainer: %s", e.getPath()); + } + } + } + } + } + + private static final class CPEFile extends File { + private static final long serialVersionUID = 1L; + + private final IClasspathEntry mClasspathEntry; + + public CPEFile(String pathname, IClasspathEntry classpathEntry) { + super(pathname); + mClasspathEntry = classpathEntry; + } + + public CPEFile(File file, IClasspathEntry classpathEntry) { + super(file.getAbsolutePath()); + mClasspathEntry = classpathEntry; + } + + public IClasspathEntry getClasspathEntry() { + return mClasspathEntry; + } + } + + private static void handleClasspathEntry(IClasspathEntry e, IWorkspaceRoot wsRoot, + Set jarFiles) { + // get the IPath + IPath path = e.getPath(); + + IResource resource = wsRoot.findMember(path); + + if (AdtConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) { + // case of a jar file (which could be relative to the workspace or a full path) + if (resource != null && resource.exists() && + resource.getType() == IResource.FILE) { + jarFiles.add(new CPEFile(resource.getLocation().toFile(), e)); + } else { + // if the jar path doesn't match a workspace resource, + // then we get an OSString and check if this links to a valid file. + String osFullPath = path.toOSString(); + + File f = new CPEFile(osFullPath, e); + if (f.isFile()) { + jarFiles.add(f); + } + } + } + } } -- cgit v1.1