diff options
Diffstat (limited to 'eclipse')
14 files changed, 500 insertions, 163 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/view.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/view.png Binary files differdeleted file mode 100644 index 35c4d8f..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/icons/view.png +++ /dev/null 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 @@ <super type="org.eclipse.core.resources.textmarker" /> <persistent value="true" /> </extension> + <extension + id="com.android.ide.eclipse.adt.adtProblem" + name="Android ADT Problem" + point="org.eclipse.core.resources.markers"> + <super type="org.eclipse.core.resources.problemmarker" /> + <super type="org.eclipse.core.resources.textmarker" /> + <persistent value="true" /> + </extension> + <extension + id="com.android.ide.eclipse.adt.targetProblem" + name="Android Target 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"> + <super type="org.eclipse.core.resources.problemmarker" /> + <persistent value="false" /> + </extension> + <extension + id="com.android.ide.eclipse.adt.packagingProblem" + name="Android Packaging Problem" + point="org.eclipse.core.resources.markers"> + <super type="org.eclipse.core.resources.problemmarker" /> + <persistent value="true" /> + </extension> <extension point="org.eclipse.ui.ide.markerResolution"> <markerResolutionGenerator markerType="com.android.ide.eclipse.adt.lintProblem" @@ -385,28 +414,6 @@ <extension point="org.eclipse.core.runtime.preferences"> <initializer class="com.android.ide.eclipse.adt.internal.preferences.AdtPrefs" /> </extension> - <extension - id="com.android.ide.eclipse.adt.adtProblem" - name="Android ADT Problem" - point="org.eclipse.core.resources.markers"> - <super type="org.eclipse.core.resources.problemmarker" /> - <super type="org.eclipse.core.resources.textmarker" /> - <persistent value="true" /> - </extension> - <extension - id="com.android.ide.eclipse.adt.targetProblem" - name="Android Target 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.packagingProblem" - name="Android Packaging Problem" - point="org.eclipse.core.resources.markers"> - <super type="org.eclipse.core.resources.problemmarker" /> - <persistent value="true" /> - </extension> <extension point="org.eclipse.ui.perspectiveExtensions"> <perspectiveExtension targetID="org.eclipse.jdt.ui.JavaPerspective"> <newWizardShortcut id="com.android.ide.eclipse.adt.project.NewProjectWizard" /> diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridDropHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridDropHandler.java index 796252b..0c7ed9f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridDropHandler.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridDropHandler.java @@ -680,7 +680,8 @@ public class GridDropHandler { // Ensure that we don't store columnCount=0 if (mGrid.actualColumnCount == 0) { - mGrid.layout.setAttribute(ANDROID_URI, ATTR_COLUMN_COUNT, VALUE_1); + mGrid.layout.setAttribute(ANDROID_URI, ATTR_COLUMN_COUNT, + Integer.toString(Math.max(1, column + 1))); } return newChild; 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 7536c2f..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 @@ -19,7 +19,6 @@ package com.android.ide.eclipse.adt.internal.build; 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.build.BuildHelper.ResourceMarker; 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; @@ -747,6 +746,12 @@ public class BuildHelper { mOutStream.setPrefix(CONSOLE_PREFIX_DX); mErrStream.setPrefix(CONSOLE_PREFIX_DX); + if (mVerbose) { + for (String input : inputPaths) { + mOutStream.println("Input: " + input); + } + } + int res = wrapper.run(osOutFilePath, inputPaths, mVerbose, @@ -794,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<String> commandArray = new ArrayList<String>(); - commandArray.add(target.getPath(IAndroidTarget.AAPT)); + commandArray.add(aapt); commandArray.add(aaptCommand); if (AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE) { commandArray.add("-v"); //$NON-NLS-1$ @@ -1061,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/build/builders/PostCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java index d65dedf..6c4eca4 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 @@ -445,11 +445,9 @@ public class PostCompilerBuilder extends BaseBuilder { IFolder javaOutputFolder = BaseProjectHelper.getJavaOutputFolder(project); - writeLibraryPackage(jarIFile, project, appPackage, javaOutputFolder, - referencedJavaProjects); + writeLibraryPackage(jarIFile, project, appPackage, javaOutputFolder); saveProjectBooleanProperty(PROPERTY_CONVERT_TO_DEX, mConvertToDex = false); - // refresh the bin folder content with no recursion to update the library // jar file. androidOutputFolder.refreshLocal(IResource.DEPTH_ONE, monitor); @@ -815,13 +813,11 @@ public class PostCompilerBuilder extends BaseBuilder { rootFolder.getFullPath()); String name = file.getName(); - // we don't package any R[$*] classes whatever the package (because we generate - // more than one if there are library dependencies). Also ignore Manifest and - // BuildConfig classes that are in the app package. - if (R_PATTERN.matcher(name).matches() || - (mAppPackage.equals(packageApp.toString()) && + // Ignore the library's R/Manifest/BuildConfig classes. + if (mAppPackage.equals(packageApp.toString()) && (BUILD_CONFIG_CLASS.equals(name) || - MANIFEST_PATTERN.matcher(name).matches()))) { + MANIFEST_PATTERN.matcher(name).matches() || + R_PATTERN.matcher(name).matches())) { return; } @@ -900,8 +896,15 @@ public class PostCompilerBuilder extends BaseBuilder { return true; } + /** + * Writes the library jar file. + * @param jarIFile the destination file + * @param project the library project + * @param appPackage the library android package + * @param javaOutputFolder the JDT output folder. + */ private void writeLibraryPackage(IFile jarIFile, IProject project, String appPackage, - IFolder javaOutputFolder, List<IJavaProject> referencedJavaProjects) { + IFolder javaOutputFolder) { JarOutputStream jos = null; try { @@ -920,26 +923,6 @@ public class PostCompilerBuilder extends BaseBuilder { // now write the standard Java resources BuildHelper.writeResources(jarBuilder, JavaCore.create(project)); - // do the same for all the referencedJava project - for (IJavaProject javaProject : referencedJavaProjects) { - // in case an Android project was referenced (which won't work), the - // best thing is to ignore this project. - if (javaProject.getProject().hasNature(AdtConstants.NATURE_DEFAULT)) { - continue; - } - - IFolder refProjectOutput = BaseProjectHelper.getJavaOutputFolder( - javaProject.getProject()); - - if (refProjectOutput != null) { - // write the class files - writeClassFilesIntoJar(jarBuilder, refProjectOutput, refProjectOutput); - - // now write the standard Java resources - BuildHelper.writeResources(jarBuilder, javaProject); - } - } - saveProjectBooleanProperty(PROPERTY_CONVERT_TO_DEX, mConvertToDex); } catch (Exception e) { AdtPlugin.log(e, "Failed to write jar file %s", jarIFile.getLocation().toOSString()); 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 7713f47..8234f25 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 @@ -805,7 +805,8 @@ public class PreCompilerBuilder extends BaseBuilder { array.add("--auto-add-overlay"); //$NON-NLS-1$ } - if (libraryPackages != null) { + // there's no need to generate the R class of the libraries if this is a library too. + if (isLibrary == false && libraryPackages != null) { array.add("--extra-packages"); //$NON-NLS-1$ array.add(libraryPackages); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParser.java index f57d904..6915052 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParser.java @@ -16,6 +16,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout; +import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_HEIGHT; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH; import static com.android.ide.common.layout.LayoutConstants.ATTR_PADDING; @@ -24,6 +25,7 @@ import static com.android.ide.common.layout.LayoutConstants.VALUE_MATCH_PARENT; import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.ATTR_LAYOUT; import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_FRAGMENT; import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_INCLUDE; +import static com.android.tools.lint.detector.api.LintConstants.AUTO_URI; import com.android.ide.common.rendering.api.ILayoutPullParser; import com.android.ide.common.rendering.api.ViewInfo; @@ -366,6 +368,15 @@ public class UiElementPullParser extends BasePullParser { } Node attribute = xmlNode.getAttributes().getNamedItemNS(namespace, localName); + + // Auto-convert http://schemas.android.com/apk/res-auto resources. The lookup + // will be for the current application's resource package, e.g. + // http://schemas.android.com/apk/res/foo.bar, but the XML document will + // be using http://schemas.android.com/apk/res-auto in library projects: + if (attribute == null && namespace != null && !namespace.equals(ANDROID_URI)) { + attribute = xmlNode.getAttributes().getNamedItemNS(AUTO_URI, localName); + } + if (attribute != null) { String value = attribute.getNodeValue(); if (mIncreaseExistingPadding && ATTR_PADDING.equals(localName) && diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java index ef1a17c..485d574 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java @@ -17,6 +17,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2; import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI; +import static com.android.ide.common.layout.LayoutConstants.ATTR_CLASS; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_COLUMN; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_COLUMN_SPAN; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ROW; @@ -28,6 +29,7 @@ import static com.android.ide.common.layout.LayoutConstants.DRAWABLE_PREFIX; import static com.android.ide.common.layout.LayoutConstants.LAYOUT_PREFIX; import static com.android.ide.common.layout.LayoutConstants.LINEAR_LAYOUT; import static com.android.ide.common.layout.LayoutConstants.VALUE_VERTICAL; +import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_VIEWTAG; import static org.eclipse.jface.viewers.StyledString.QUALIFIER_STYLER; import com.android.annotations.VisibleForTesting; @@ -496,13 +498,26 @@ public class OutlinePage extends ContentOutlinePage Image img = null; // Special case for the common case of vertical linear layouts: // show vertical linear icon (the default icon shows horizontal orientation) - if (desc.getUiName().equals(LINEAR_LAYOUT)) { + String uiName = desc.getUiName(); + if (uiName.equals(LINEAR_LAYOUT)) { Element e = (Element) node.getXmlNode(); if (VALUE_VERTICAL.equals(e.getAttributeNS(ANDROID_URI, ATTR_ORIENTATION))) { IconFactory factory = IconFactory.getInstance(); img = factory.getIcon("VerticalLinearLayout"); //$NON-NLS-1$ } + } else if (uiName.equals(VIEW_VIEWTAG)) { + Node xmlNode = node.getXmlNode(); + if (xmlNode instanceof Element) { + String className = ((Element) xmlNode).getAttribute(ATTR_CLASS); + if (className != null && className.length() > 0) { + int index = className.lastIndexOf('.'); + if (index != -1) { + className = className.substring(index + 1); + } + img = IconFactory.getInstance().getIcon(className); + } + } } if (img == null) { img = desc.getGenericIcon(); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestEditor.java index a8ebe75..8f34b48 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestEditor.java @@ -35,7 +35,6 @@ import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor; import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IFileListener; import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; -import com.android.sdklib.xml.AndroidXPathFactory; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; @@ -57,10 +56,6 @@ import org.w3c.dom.Node; import java.util.Collection; import java.util.List; -import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathConstants; -import javax.xml.xpath.XPathExpressionException; - /** * Multi-page form editor for AndroidManifest.xml. */ @@ -189,18 +184,24 @@ public final class ManifestEditor extends AndroidXmlEditor { private Node getManifestXmlNode(Document xmlDoc) { if (xmlDoc != null) { - ElementDescriptor manifest_desc = mUiManifestNode.getDescriptor(); - try { - XPath xpath = AndroidXPathFactory.newXPath(); - Node node = (Node) xpath.evaluate("/" + manifest_desc.getXmlName(), //$NON-NLS-1$ - xmlDoc, - XPathConstants.NODE); - assert node != null && node.getNodeName().equals(manifest_desc.getXmlName()); - - return node; - } catch (XPathExpressionException e) { - AdtPlugin.log(e, "XPath error when trying to find '%s' element in XML.", //$NON-NLS-1$ - manifest_desc.getXmlName()); + ElementDescriptor manifestDesc = mUiManifestNode.getDescriptor(); + String manifestXmlName = manifestDesc == null ? null : manifestDesc.getXmlName(); + assert manifestXmlName != null; + + if (manifestXmlName != null) { + Node node = xmlDoc.getDocumentElement(); + if (node != null && manifestXmlName.equals(node.getNodeName())) { + return node; + } + + for (node = xmlDoc.getFirstChild(); + node != null; + node = node.getNextSibling()) { + if (node.getNodeType() == Node.ELEMENT_NODE && + manifestXmlName.equals(node.getNodeName())) { + return node; + } + } } } 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 = "<null>"; //$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 7b6c9b7..197d18c 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,9 +44,11 @@ import org.eclipse.jdt.core.JavaModelException; import java.io.File; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; -public class LibraryClasspathContainerInitializer extends ClasspathContainerInitializer { +public class LibraryClasspathContainerInitializer extends BaseClasspathContainerInitializer { public LibraryClasspathContainerInitializer() { } @@ -60,7 +66,7 @@ public class LibraryClasspathContainerInitializer extends ClasspathContainerInit IClasspathContainer[] containers = new IClasspathContainer[projectCount]; for (int i = 0 ; i < projectCount; i++) { - containers[i] = allocateAndroidContainer(androidProjects[i]); + containers[i] = allocateLibraryContainer(androidProjects[i]); } // give each project their new container in one call. @@ -97,7 +103,7 @@ public class LibraryClasspathContainerInitializer extends ClasspathContainerInit @Override public void initialize(IPath containerPath, IJavaProject project) throws CoreException { if (AdtConstants.CONTAINER_LIBRARIES.equals(containerPath.toString())) { - IClasspathContainer container = allocateAndroidContainer(project); + IClasspathContainer container = allocateLibraryContainer(project); if (container != null) { JavaCore.setClasspathContainer(new Path(AdtConstants.CONTAINER_LIBRARIES), new IJavaProject[] { project }, @@ -107,7 +113,7 @@ public class LibraryClasspathContainerInitializer extends ClasspathContainerInit } } - private static IClasspathContainer allocateAndroidContainer(IJavaProject javaProject) { + private static IClasspathContainer allocateLibraryContainer(IJavaProject javaProject) { final IProject iProject = javaProject.getProject(); AdtPlugin plugin = AdtPlugin.getDefault(); @@ -115,8 +121,8 @@ public class LibraryClasspathContainerInitializer extends ClasspathContainerInit return null; } + // First check that the project has a library-type container. try { - // First check that the project has a library-type container. IClasspathEntry[] rawClasspath = javaProject.getRawClasspath(); IClasspathEntry[] oldRawClasspath = rawClasspath; @@ -138,7 +144,8 @@ public class LibraryClasspathContainerInitializer extends ClasspathContainerInit if (foundLibrariesContainer == false) { // add the android container to the array rawClasspath = ProjectHelper.addEntryToClasspath(rawClasspath, - JavaCore.newContainerEntry(new Path(AdtConstants.CONTAINER_LIBRARIES))); + JavaCore.newContainerEntry(new Path(AdtConstants.CONTAINER_LIBRARIES), + true /*isExported*/)); } // set the new list of entries to the project @@ -154,10 +161,29 @@ public class LibraryClasspathContainerInitializer extends ClasspathContainerInit // check if the project has a valid target. ProjectState state = Sdk.getProjectState(iProject); + /* + * At this point we're going to gather a list of all that need to go in the + * dependency container. + * - Library project outputs (direct and indirect) + * - Java project output (those can be indirectly referenced through library projects + * or other other Java projects) + * - Jar files: + * + inside this project's libs/ + * + inside the library projects' libs/ + * + inside the referenced Java projects' classpath + */ + List<IClasspathEntry> entries = new ArrayList<IClasspathEntry>(); IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); + // list of java project dependencies and jar files that will be built while + // going through the library projects. + Set<File> jarFiles = new HashSet<File>(); + Set<IProject> refProjects = new HashSet<IProject>(); + + // process all the libraries + List<IProject> libProjects = state.getFullLibraryProjects(); for (IProject libProject : libProjects) { // get the project output @@ -179,34 +205,223 @@ public class LibraryClasspathContainerInitializer extends ClasspathContainerInit } } + // we can directly add a CPE for this jar as there's no risk of a duplicate. IClasspathEntry entry = JavaCore.newLibraryEntry( jarIFile.getLocation(), sourceFolder, // source attachment path - null); // default source attachment root path. + null, // default source attachment root path. + true /*isExported*/); entries.add(entry); + + // now, gather the content of this library project's libs folder. + getJarListFromLibsFolder(libProject, jarFiles); } + + // get project dependencies + processReferencedProjects(libProject, refProjects, jarFiles); } + // now process this projects' referenced projects + processReferencedProjects(iProject, refProjects, jarFiles); + // and its own jar files from libs + getJarListFromLibsFolder(iProject, jarFiles); + // annotations support for older version of android if (state.getTarget() != null && state.getTarget().getVersion().getApiLevel() <= 15) { File annotationsJar = new File(Sdk.getCurrent().getSdkLocation(), 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); + } + + // 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(), true /*isExported*/)); + } + + // 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; - entries.add(0, entry); + try { + List<File> 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(), + true /*isExported*/)); + } else { + entries.add(JavaCore.newLibraryEntry(new Path(jarFile.getAbsolutePath()), + null /*sourceAttachmentPath*/, null /*sourceAttachmentRootPath*/, + true /*isExported*/)); + } + } + } 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), - "Library Projects", + "Android Dependencies", IClasspathContainer.K_APPLICATION); } + + private static void processReferencedProjects(IProject project, + Set<IProject> projects, Set<File> jarFiles) { + 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); + + // get the jar dependencies of the project in the list + getJarListFromClasspath(p, jarFiles); + + // and then process the referenced project by this project too. + processReferencedProjects(p, projects, jarFiles); + } + } + } catch (CoreException e) { + // can't get the referenced projects? ignore + } + } + + /** + * Finds all the jar files inside a project's libs folder. + * @param project + * @param jarFiles + */ + private static void getJarListFromLibsFolder(IProject project, Set<File> jarFiles) { + IFolder libsFolder = project.getFolder(SdkConstants.FD_NATIVE_LIBS); + if (libsFolder.exists()) { + try { + IResource[] members = libsFolder.members(); + for (IResource member : members) { + if (member.getType() == IResource.FILE && + AdtConstants.EXT_JAR.equalsIgnoreCase(member.getFileExtension())) { + jarFiles.add(member.getLocation().toFile()); + } + } + } catch (CoreException e) { + // can't get the list? ignore this folder. + } + } + } + + /** + * Finds all the jars a given project depends on by looking at the classpath. + * This must be a non android project, as android project have container that really + * we shouldn't go through. + * @param project the project to query + * @param jarFiles the list of file to fill. + */ + private static void getJarListFromClasspath(IProject project, Set<File> jarFiles) { + IJavaProject javaProject = JavaCore.create(project); + 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) { + // 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<File> 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); + } + } + } + } } 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 092ffb5..63f381f 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 @@ -83,6 +83,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -712,7 +713,8 @@ public final class Sdk { // is called back during registration with project opened in the workspace. monitor.addResourceEventListener(mResourceEventListener); monitor.addProjectListener(mProjectListener); - monitor.addFileListener(mFileListener, IResourceDelta.CHANGED | IResourceDelta.ADDED); + monitor.addFileListener(mFileListener, + IResourceDelta.CHANGED | IResourceDelta.ADDED | IResourceDelta.REMOVED); // pre-compute some paths mDocBaseUrl = getDocumentationBaseUrl(mManager.getLocation() + @@ -1062,6 +1064,47 @@ public final class Sdk { // This can't happen as it's only for closed project (or non existing) // but in that case we can't get a fileChanged on this file. } + } else if (kind == IResourceDelta.ADDED || kind == IResourceDelta.REMOVED) { + // check if it's an add/remove on a jar files inside libs + if (file.getProjectRelativePath().segmentCount() == 2 && + file.getParent().getName().equals(SdkConstants.FD_NATIVE_LIBS)) { + // need to update the project and whatever depend on it. + + processJarFileChange(file); + } + } + } + + private void processJarFileChange(final IFile file) { + try { + IProject iProject = file.getProject(); + + if (iProject.hasNature(AdtConstants.NATURE_DEFAULT) == false) { + return; + } + + List<IJavaProject> projectList = new ArrayList<IJavaProject>(); + IJavaProject javaProject = BaseProjectHelper.getJavaProject(iProject); + if (javaProject != null) { + projectList.add(javaProject); + } + + ProjectState state = Sdk.getProjectState(iProject); + + Collection<ProjectState> parents = state.getFullParentProjects(); + for (ProjectState s : parents) { + javaProject = BaseProjectHelper.getJavaProject(s.getProject()); + if (javaProject != null) { + projectList.add(javaProject); + } + } + + ProjectHelper.updateProjects( + projectList.toArray(new IJavaProject[projectList.size()])); + + } catch (CoreException e) { + // This can't happen as it's only for closed project (or non existing) + // but in that case we can't get a fileChanged on this file. } } }; |