From 610a7584cd2ede40772dbe95bd59e525a3859837 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Thu, 18 Feb 2010 19:52:14 -0800 Subject: ADT: Library support: source folder and pre-compiler. This is the first step in the library support. For each library, create a source folder in the main project that is linked to the source folder of the library project. The linked resources use a path variable named after the library in the format: _android_. These variables are always created when the link is created. For now the link is recreated all the time, but we could do a check and not redo it if it's already done. Additionally, the pre-compiler creates the R class from the res folders of the main and library projects. Some misc fixes/clean-ups: * Fix an issue with the new ProjectState where opening a project would not trigger a load of its target data. * Changed the lock for all SDK operation: - moved the lock in Sdk accessible as Sdk.getLock() - made the few Sdk method that used their own synchronize block use the same lock as all others. * removed the builders project and moved its content to sdklib This was meant as a way to share code between the Eclipse builders and the Ant tasks but sdklib is already used by both, so it's better to put the code in sdklib than have yet another project. Change-Id: Ibfa449c7a809f28e428c03bbda8215969717ecde --- anttasks/src/com/android/ant/SetupTask.java | 14 +- builders/.classpath | 6 - builders/.gitignore | 1 - builders/.project | 17 - builders/Android.mk | 17 - builders/src/Android.mk | 24 -- builders/src/com/android/builders/FileWrapper.java | 79 ---- .../src/com/android/builders/FolderWrapper.java | 68 ---- .../src/com/android/builders/IAbstractFile.java | 36 -- .../src/com/android/builders/IAbstractFolder.java | 31 -- .../com/android/builders/IAbstractResource.java | 30 -- .../src/com/android/builders/StreamException.java | 28 -- .../plugins/com.android.ide.eclipse.adt/.classpath | 1 - .../META-INF/MANIFEST.MF | 7 +- .../com.android.ide.eclipse.adt/build.properties | 3 +- .../src/com/android/ide/eclipse/adt/AdtPlugin.java | 29 +- .../adt/internal/build/PreCompilerBuilder.java | 416 +++++++++++++-------- .../AndroidClasspathContainerInitializer.java | 12 +- .../internal/project/AndroidManifestParser.java | 22 -- .../eclipse/adt/internal/project/ProjectState.java | 126 +++++++ .../resources/manager/MultiResourceFile.java | 4 +- .../resources/manager/ProjectResources.java | 2 +- .../internal/resources/manager/ResourceFile.java | 18 +- .../internal/resources/manager/ResourceFolder.java | 4 +- .../resources/manager/ResourceManager.java | 8 +- .../resources/manager/SingleResourceFile.java | 2 +- .../resources/manager/files/IFileWrapper.java | 8 +- .../resources/manager/files/IFolderWrapper.java | 39 +- .../android/ide/eclipse/adt/internal/sdk/Sdk.java | 254 ++++++++++--- .../adt/internal/ui/ResourceExplorerView.java | 2 +- .../wizards/newproject/NewProjectCreationPage.java | 3 +- .../com.android.ide.eclipse.tests/.classpath | 1 - .../com/android/ide/eclipse/tests/SdkTestCase.java | 2 +- .../editors/resources/manager/ConfigMatchTest.java | 2 +- eclipse/scripts/create_adt_symlinks.sh | 2 +- .../android/sdklib/internal/io/FileWrapper.java | 78 ++++ .../android/sdklib/internal/io/FolderWrapper.java | 70 ++++ .../android/sdklib/internal/io/IAbstractFile.java | 36 ++ .../sdklib/internal/io/IAbstractFolder.java | 38 ++ .../sdklib/internal/io/IAbstractResource.java | 35 ++ .../sdklib/internal/io/StreamException.java | 28 ++ .../com/android/sdklib/xml/AndroidManifest.java | 46 +++ 42 files changed, 1002 insertions(+), 647 deletions(-) delete mode 100644 builders/.classpath delete mode 100644 builders/.gitignore delete mode 100644 builders/.project delete mode 100644 builders/Android.mk delete mode 100644 builders/src/Android.mk delete mode 100644 builders/src/com/android/builders/FileWrapper.java delete mode 100644 builders/src/com/android/builders/FolderWrapper.java delete mode 100644 builders/src/com/android/builders/IAbstractFile.java delete mode 100644 builders/src/com/android/builders/IAbstractFolder.java delete mode 100644 builders/src/com/android/builders/IAbstractResource.java delete mode 100644 builders/src/com/android/builders/StreamException.java create mode 100644 sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/FileWrapper.java create mode 100644 sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/FolderWrapper.java create mode 100644 sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/IAbstractFile.java create mode 100644 sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/IAbstractFolder.java create mode 100644 sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/IAbstractResource.java create mode 100644 sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/StreamException.java diff --git a/anttasks/src/com/android/ant/SetupTask.java b/anttasks/src/com/android/ant/SetupTask.java index 790c068..f25d09f 100644 --- a/anttasks/src/com/android/ant/SetupTask.java +++ b/anttasks/src/com/android/ant/SetupTask.java @@ -22,6 +22,7 @@ import com.android.sdklib.ISdkLog; import com.android.sdklib.SdkConstants; import com.android.sdklib.SdkManager; import com.android.sdklib.IAndroidTarget.IOptionalLibrary; +import com.android.sdklib.internal.io.FileWrapper; import com.android.sdklib.internal.project.ProjectProperties; import com.android.sdklib.internal.project.ProjectProperties.PropertyType; import com.android.sdklib.xml.AndroidManifest; @@ -419,24 +420,15 @@ public final class SetupTask extends ImportTask { // get the package from the manifest. File manifest = new File(rootPath, SdkConstants.FN_ANDROID_MANIFEST_XML); - XPath xPath = AndroidXPathFactory.newXPath(); - - // check the package name. try { - String value = xPath.evaluate( - "/" + AndroidManifest.NODE_MANIFEST + - "/@" + AndroidManifest.ATTRIBUTE_PACKAGE, - new InputSource(new FileInputStream(manifest))); + String value = AndroidManifest.getPackage(new FileWrapper(manifest)); if (value != null) { // aapt will complain if it's missing. sb.append(';'); sb.append(value); } - } catch (XPathExpressionException e) { - throw new BuildException(e); - } catch (FileNotFoundException e) { + } catch (Exception e) { throw new BuildException(e); } - } // even with no libraries, always setup these so that various tasks in Ant don't complain diff --git a/builders/.classpath b/builders/.classpath deleted file mode 100644 index fb50116..0000000 --- a/builders/.classpath +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/builders/.gitignore b/builders/.gitignore deleted file mode 100644 index c5e82d7..0000000 --- a/builders/.gitignore +++ /dev/null @@ -1 +0,0 @@ -bin \ No newline at end of file diff --git a/builders/.project b/builders/.project deleted file mode 100644 index 713c9a2..0000000 --- a/builders/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - builders - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/builders/Android.mk b/builders/Android.mk deleted file mode 100644 index 7e69334..0000000 --- a/builders/Android.mk +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (C) 2010 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -BUILDERS_LOCAL_DIR := $(call my-dir) -include $(BUILDERS_LOCAL_DIR)/src/Android.mk diff --git a/builders/src/Android.mk b/builders/src/Android.mk deleted file mode 100644 index aaa6864..0000000 --- a/builders/src/Android.mk +++ /dev/null @@ -1,24 +0,0 @@ -# -# Copyright (C) 2010 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_JAVA_LIBRARIES := \ - -LOCAL_MODULE := builders - -include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/builders/src/com/android/builders/FileWrapper.java b/builders/src/com/android/builders/FileWrapper.java deleted file mode 100644 index a564cae..0000000 --- a/builders/src/com/android/builders/FileWrapper.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.builders; - - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; - -/** - * An implementation of {@link IAbstractFile} on top of a {@link File} object. - * - */ -public class FileWrapper implements IAbstractFile { - - private final File mFile; - - /** - * Constructs a {@link FileWrapper} object. If {@link File#isFile()} returns false - * then an {@link IOException} is thrown. - */ - public FileWrapper(File file) throws IOException { - if (file.isFile() == false) { - throw new IOException("FileWrapper must wrap a File object representing an existing file!"); //$NON-NLS-1$ - } - - mFile = file; - } - - public InputStream getContents() throws StreamException { - try { - return new FileInputStream(mFile); - } catch (FileNotFoundException e) { - throw new StreamException(e); - } - } - - public String getOsLocation() { - return mFile.getAbsolutePath(); - } - - public String getName() { - return mFile.getName(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof FileWrapper) { - return mFile.equals(((FileWrapper)obj).mFile); - } - - if (obj instanceof File) { - return mFile.equals(obj); - } - - return super.equals(obj); - } - - @Override - public int hashCode() { - return mFile.hashCode(); - } -} diff --git a/builders/src/com/android/builders/FolderWrapper.java b/builders/src/com/android/builders/FolderWrapper.java deleted file mode 100644 index 6d092c7..0000000 --- a/builders/src/com/android/builders/FolderWrapper.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.builders; - - -import java.io.File; -import java.io.IOException; - -/** - * An implementation of {@link IAbstractFolder} on top of a {@link File} object. - */ -public class FolderWrapper implements IAbstractFolder { - - private final File mFolder; - - /** - * Constructs a {@link FileWrapper} object. If {@link File#isDirectory()} returns - * false then an {@link IOException} is thrown. - */ - public FolderWrapper(File folder) throws IOException { - if (folder.isDirectory() == false) { - throw new IOException("FileWrapper must wrap a File object representing an existing folder!"); //$NON-NLS-1$ - } - - mFolder = folder; - } - - public boolean hasFile(String name) { - return false; - } - - public String getName() { - return mFolder.getName(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof FolderWrapper) { - return mFolder.equals(((FolderWrapper)obj).mFolder); - } - - if (obj instanceof File) { - return mFolder.equals(obj); - } - - return super.equals(obj); - } - - @Override - public int hashCode() { - return mFolder.hashCode(); - } - -} diff --git a/builders/src/com/android/builders/IAbstractFile.java b/builders/src/com/android/builders/IAbstractFile.java deleted file mode 100644 index 008d4b1..0000000 --- a/builders/src/com/android/builders/IAbstractFile.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.builders; - -import java.io.InputStream; - -/** - * A file. - */ -public interface IAbstractFile extends IAbstractResource { - - /** - * Returns an {@link InputStream} object on the file content. - * @throws CoreException - */ - InputStream getContents() throws StreamException; - - /** - * Returns the OS path of the file location. - */ - String getOsLocation(); -} diff --git a/builders/src/com/android/builders/IAbstractFolder.java b/builders/src/com/android/builders/IAbstractFolder.java deleted file mode 100644 index 92407ae..0000000 --- a/builders/src/com/android/builders/IAbstractFolder.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.builders; - - -/** - * A folder. - */ -public interface IAbstractFolder extends IAbstractResource { - - /** - * Returns true if the receiver contains a file with a given name - * @param name the name of the file. This is the name without the path leading to the - * parent folder. - */ - boolean hasFile(String name); -} diff --git a/builders/src/com/android/builders/IAbstractResource.java b/builders/src/com/android/builders/IAbstractResource.java deleted file mode 100644 index e573f84..0000000 --- a/builders/src/com/android/builders/IAbstractResource.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.builders; - -/** - * Base representation of a file system resource.

- * This somewhat limited interface is designed to let classes use file-system resources, without - * having the manually handle either the standard Java file or the Eclipse file API.. - */ -public interface IAbstractResource { - - /** - * Returns the name of the resource. - */ - String getName(); -} diff --git a/builders/src/com/android/builders/StreamException.java b/builders/src/com/android/builders/StreamException.java deleted file mode 100644 index 4db2df3..0000000 --- a/builders/src/com/android/builders/StreamException.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.builders; - -/** - * Exception thrown when {@link IAbstractFile#getContents()} fails. - */ -public class StreamException extends Exception { - private static final long serialVersionUID = 1L; - - public StreamException(Exception e) { - super(e); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/.classpath b/eclipse/plugins/com.android.ide.eclipse.adt/.classpath index 91dbcbb..2fd96a4 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/.classpath +++ b/eclipse/plugins/com.android.ide.eclipse.adt/.classpath @@ -14,6 +14,5 @@ - diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF index 46d97b5..9a1b9cd 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF +++ b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF @@ -14,8 +14,7 @@ Bundle-ClassPath: ., sdklib.jar, sdkuilib.jar, commons-compress-1.0.jar, - groovy-all-1.7.0.jar, - builders.jar + groovy-all-1.7.0.jar Bundle-Activator: com.android.ide.eclipse.adt.AdtPlugin Bundle-Vendor: The Android Open Source Project Require-Bundle: com.android.ide.eclipse.ddms, @@ -49,8 +48,7 @@ Require-Bundle: com.android.ide.eclipse.ddms, org.eclipse.ltk.ui.refactoring, org.eclipse.core.expressions Eclipse-LazyStart: true -Export-Package: com.android.builders;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.adt;x-friends:="com.android.ide.eclipse.tests", +Export-Package: com.android.ide.eclipse.adt;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.internal;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.internal.actions;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.internal.build;x-friends:="com.android.ide.eclipse.tests", @@ -100,6 +98,7 @@ Export-Package: com.android.builders;x-friends:="com.android.ide.eclipse.tests", com.android.prefs;x-friends:="com.android.ide.eclipse.tests", com.android.sdklib;x-friends:="com.android.ide.eclipse.tests", com.android.sdklib.internal.avd;x-friends:="com.android.ide.eclipse.tests", + com.android.sdklib.internal.io;x-friends:="com.android.ide.eclipse.tests", com.android.sdklib.internal.project;x-friends:="com.android.ide.eclipse.tests", com.android.sdklib.internal.repository;x-friends:="com.android.ide.eclipse.tests", com.android.sdklib.repository;x-friends:="com.android.ide.eclipse.tests", diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/build.properties b/eclipse/plugins/com.android.ide.eclipse.adt/build.properties index ed2a914..47f6611 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/build.properties +++ b/eclipse/plugins/com.android.ide.eclipse.adt/build.properties @@ -15,7 +15,6 @@ bin.includes = plugin.xml,\ sdkuilib.jar,\ commons-compress-1.0.jar,\ groovy-all-1.7.0.jar,\ - gscripts/,\ - builders.jar + gscripts/ source.. = src/ output.. = bin/ 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 af211b9..f52e9c4 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 @@ -34,11 +34,11 @@ import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; import com.android.ide.eclipse.adt.internal.project.ExportHelper; import com.android.ide.eclipse.adt.internal.project.ProjectHelper; import com.android.ide.eclipse.adt.internal.project.ExportHelper.IExportCallback; +import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor; import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources; import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolder; import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType; import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; -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.LoadStatus; import com.android.ide.eclipse.adt.internal.sdk.Sdk; @@ -817,25 +817,17 @@ public class AdtPlugin extends AbstractUIPlugin { * Returns whether the {@link IAndroidTarget}s have been loaded from the SDK. */ public final LoadStatus getSdkLoadStatus() { - synchronized (getSdkLockObject()) { + synchronized (Sdk.getLock()) { return mSdkIsLoaded; } } /** - * Returns the lock object for SDK loading. If you wish to do things while the SDK is loading, - * you must synchronize on this object. - */ - public final Object getSdkLockObject() { - return mPostLoadProjectsToResolve; - } - - /** * Sets the given {@link IJavaProject} to have its target resolved again once the SDK finishes * to load. */ public final void setProjectToResolve(IJavaProject javaProject) { - synchronized (getSdkLockObject()) { + synchronized (Sdk.getLock()) { mPostLoadProjectsToResolve.add(javaProject); } } @@ -847,7 +839,7 @@ public class AdtPlugin extends AbstractUIPlugin { */ public final void setProjectToCheck(IJavaProject javaProject) { // only lock on - synchronized (getSdkLockObject()) { + synchronized (Sdk.getLock()) { mPostLoadProjectsToCheck.add(javaProject); } } @@ -984,13 +976,18 @@ public class AdtPlugin extends AbstractUIPlugin { if (sdk != null) { ArrayList list = new ArrayList(); - synchronized (getSdkLockObject()) { + synchronized (Sdk.getLock()) { mSdkIsLoaded = LoadStatus.LOADED; progress.setTaskName("Check Projects"); for (IJavaProject javaProject : mPostLoadProjectsToResolve) { - if (javaProject.getProject().isOpen()) { + IProject iProject = javaProject.getProject(); + if (iProject.isOpen()) { + // 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.getProject(iProject)); list.add(javaProject); } } @@ -1018,7 +1015,7 @@ public class AdtPlugin extends AbstractUIPlugin { } else { // SDK failed to Load! // Sdk#loadSdk() has already displayed an error. - synchronized (getSdkLockObject()) { + synchronized (Sdk.getLock()) { mSdkIsLoaded = LoadStatus.FAILED; } } @@ -1372,7 +1369,7 @@ public class AdtPlugin extends AbstractUIPlugin { public void reparseSdk() { // add all the opened Android projects to the list of projects to be updated // after the SDK is reloaded - synchronized (getSdkLockObject()) { + synchronized (Sdk.getLock()) { // get the project to refresh. IJavaProject[] androidProjects = BaseProjectHelper.getAndroidProjects(); mPostLoadProjectsToResolve.addAll(Arrays.asList(androidProjects)); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/PreCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/PreCompilerBuilder.java index ee85e1b..e06eda2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/PreCompilerBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/PreCompilerBuilder.java @@ -23,7 +23,9 @@ import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity; import com.android.ide.eclipse.adt.internal.project.AndroidManifestParser; import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; import com.android.ide.eclipse.adt.internal.project.FixLaunchConfig; +import com.android.ide.eclipse.adt.internal.project.ProjectState; import com.android.ide.eclipse.adt.internal.project.XmlErrorHandler.BasicXmlErrorListener; +import com.android.ide.eclipse.adt.internal.resources.manager.files.IFolderWrapper; import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.sdklib.AndroidVersion; import com.android.sdklib.IAndroidTarget; @@ -207,21 +209,27 @@ public class PreCompilerBuilder extends BaseBuilder { @Override protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { + // get a project object + IProject project = getProject(); + + // list of referenced projects. + IProject[] libProjects = null; + try { mDerivedProgressMonitor.reset(); - // First thing we do is go through the resource delta to not - // lose it if we have to abort the build for any reason. + // get the project info + ProjectState projectState = Sdk.getProject(project); + IAndroidTarget projectTarget = projectState.getTarget(); + + // get the libraries + libProjects = projectState.getLibraryProjects(); - // get the project objects - IProject project = getProject(); IJavaProject javaProject = JavaCore.create(project); // Top level check to make sure the build can move forward. abortOnBadSetup(javaProject); - IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project); - // now we need to get the classpath list ArrayList sourceFolderPathList = BaseProjectHelper.getSourceClasspaths( javaProject); @@ -452,151 +460,7 @@ public class PreCompilerBuilder extends BaseBuilder { } if (mMustCompileResources) { - // we need to figure out where to store the R class. - // get the parent folder for R.java and update mManifestPackageSourceFolder - IFolder packageFolder = getGenManifestPackageFolder(project); - - // get the resource folder - IFolder resFolder = project.getFolder(AndroidConstants.WS_RESOURCES); - - // get the file system path - IPath outputLocation = mGenFolder.getLocation(); - IPath resLocation = resFolder.getLocation(); - IPath manifestLocation = manifest == null ? null : manifest.getLocation(); - - // those locations have to exist for us to do something! - if (outputLocation != null && resLocation != null - && manifestLocation != null) { - String osOutputPath = outputLocation.toOSString(); - String osResPath = resLocation.toOSString(); - String osManifestPath = manifestLocation.toOSString(); - - // remove the aapt markers - removeMarkersFromFile(manifest, AndroidConstants.MARKER_AAPT_COMPILE); - removeMarkersFromContainer(resFolder, AndroidConstants.MARKER_AAPT_COMPILE); - - AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, - Messages.Preparing_Generated_Files); - - // since the R.java file may be already existing in read-only - // mode we need to make it readable so that aapt can overwrite - // it - IFile rJavaFile = packageFolder.getFile(AndroidConstants.FN_RESOURCE_CLASS); - - // do the same for the Manifest.java class - IFile manifestJavaFile = packageFolder.getFile( - AndroidConstants.FN_MANIFEST_CLASS); - - // we actually need to delete the manifest.java as it may become empty and - // in this case aapt doesn't generate an empty one, but instead doesn't - // touch it. - manifestJavaFile.delete(true, null); - - // launch aapt: create the command line - ArrayList array = new ArrayList(); - array.add(projectTarget.getPath(IAndroidTarget.AAPT)); - array.add("package"); //$NON-NLS-1$ - array.add("-m"); //$NON-NLS-1$ - if (AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE) { - array.add("-v"); //$NON-NLS-1$ - } - array.add("-J"); //$NON-NLS-1$ - array.add(osOutputPath); - array.add("-M"); //$NON-NLS-1$ - array.add(osManifestPath); - array.add("-S"); //$NON-NLS-1$ - array.add(osResPath); - array.add("-I"); //$NON-NLS-1$ - array.add(projectTarget.getPath(IAndroidTarget.ANDROID_JAR)); - - if (AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE) { - StringBuilder sb = new StringBuilder(); - for (String c : array) { - sb.append(c); - sb.append(' '); - } - String cmd_line = sb.toString(); - AdtPlugin.printToConsole(project, cmd_line); - } - - // launch - int execError = 1; - try { - // launch the command line process - Process process = Runtime.getRuntime().exec( - array.toArray(new String[array.size()])); - - // list to store each line of stderr - ArrayList results = new ArrayList(); - - // get the output and return code from the process - execError = grabProcessOutput(process, results); - - // attempt to parse the error output - boolean parsingError = parseAaptOutput(results, project); - - // if we couldn't parse the output we display it in the console. - if (parsingError) { - if (execError != 0) { - AdtPlugin.printErrorToConsole(project, results.toArray()); - } else { - AdtPlugin.printBuildToConsole(BuildVerbosity.NORMAL, - project, results.toArray()); - } - } - - if (execError != 0) { - // if the exec failed, and we couldn't parse the error output - // (and therefore not all files that should have been marked, - // were marked), we put a generic marker on the project and abort. - if (parsingError) { - markProject(AndroidConstants.MARKER_ADT, - Messages.Unparsed_AAPT_Errors, IMarker.SEVERITY_ERROR); - } - - AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, - Messages.AAPT_Error); - - // abort if exec failed. - // This interrupts the build. The next builders will not run. - stopBuild(Messages.AAPT_Error); - } - } catch (IOException e1) { - // something happen while executing the process, - // mark the project and exit - String msg = String.format(Messages.AAPT_Exec_Error, array.get(0)); - markProject(AndroidConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); - - // This interrupts the build. The next builders will not run. - stopBuild(msg); - } catch (InterruptedException e) { - // we got interrupted waiting for the process to end... - // mark the project and exit - String msg = String.format(Messages.AAPT_Exec_Error, array.get(0)); - markProject(AndroidConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); - - // This interrupts the build. The next builders will not run. - stopBuild(msg); - } - - // if the return code was OK, we refresh the folder that - // contains R.java to force a java recompile. - if (execError == 0) { - // now add the R.java/Manifest.java to the list of file to be marked - // as derived. - mDerivedProgressMonitor.addFile(rJavaFile); - mDerivedProgressMonitor.addFile(manifestJavaFile); - - // build has been done. reset the state of the builder - mMustCompileResources = false; - - // and store it - saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, - mMustCompileResources); - } - } - } else { - // nothing to do + handleResources(project, javaPackage, projectTarget, manifest, libProjects); } // now handle the aidl stuff. @@ -612,7 +476,7 @@ public class PreCompilerBuilder extends BaseBuilder { mGenFolder.refreshLocal(IResource.DEPTH_INFINITE, mDerivedProgressMonitor); } - return null; + return libProjects; } @Override @@ -628,7 +492,6 @@ public class PreCompilerBuilder extends BaseBuilder { // remove all the derived resources from the 'gen' source folder. removeDerivedResources(mGenFolder, monitor); - // Clear the project of the generic markers removeMarkersFromProject(project, AndroidConstants.MARKER_AAPT_COMPILE); removeMarkersFromProject(project, AndroidConstants.MARKER_XML); @@ -666,6 +529,231 @@ public class PreCompilerBuilder extends BaseBuilder { } /** + * Handles resource changes and regenerate whatever files need regenerating. + * @param project the main project + * @param javaPackage the app package for the main project + * @param projectTarget the target of the main project + * @param manifest the {@link IFile} representing the project manifest + * @param libProjects the library dependencies + * @throws CoreException + */ + private void handleResources(IProject project, String javaPackage, IAndroidTarget projectTarget, + IFile manifest, IProject[] libProjects) throws CoreException { + // get the resource folder + IFolder resFolder = project.getFolder(AndroidConstants.WS_RESOURCES); + + // get the file system path + IPath outputLocation = mGenFolder.getLocation(); + IPath resLocation = resFolder.getLocation(); + IPath manifestLocation = manifest == null ? null : manifest.getLocation(); + + // those locations have to exist for us to do something! + if (outputLocation != null && resLocation != null + && manifestLocation != null) { + String osOutputPath = outputLocation.toOSString(); + String osResPath = resLocation.toOSString(); + String osManifestPath = manifestLocation.toOSString(); + + // remove the aapt markers + removeMarkersFromFile(manifest, AndroidConstants.MARKER_AAPT_COMPILE); + removeMarkersFromContainer(resFolder, AndroidConstants.MARKER_AAPT_COMPILE); + + AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, + Messages.Preparing_Generated_Files); + + // we need to figure out where to store the R class. + // get the parent folder for R.java and update mManifestPackageSourceFolder + IFolder mainPackageFolder = getGenManifestPackageFolder(); + + // handle libraries + ArrayList libResFolders = new ArrayList(); + ArrayList libOutputFolders = new ArrayList(); + ArrayList libJavaPackages = new ArrayList(); + if (libProjects != null) { + for (IProject lib : libProjects) { + IFolder libResFolder = lib.getFolder(SdkConstants.FD_RES); + if (libResFolder.exists()) { + libResFolders.add(libResFolder); + } + + try { + String libJavaPackage = AndroidManifest.getPackage(new IFolderWrapper(lib)); + if (libJavaPackage.equals(javaPackage) == false) { + libJavaPackages.add(libJavaPackage); + libOutputFolders.add(getGenManifestPackageFolder(libJavaPackage)); + } + } catch (Exception e) { + } + } + } + + execAapt(project, projectTarget, osOutputPath, osResPath, osManifestPath, + mainPackageFolder, libResFolders, null /* custom java package */); + + final int count = libOutputFolders.size(); + if (count > 0) { + for (int i = 0 ; i < count ; i++) { + IFolder libFolder = libOutputFolders.get(i); + String libJavaPackage = libJavaPackages.get(i); + execAapt(project, projectTarget, osOutputPath, osResPath, osManifestPath, + libFolder, libResFolders, libJavaPackage); + } + } + } + } + + /** + * Executes AAPT to generate R.java/Manifest.java + * @param project the main project + * @param projectTarget the main project target + * @param osOutputPath the OS output path for the generated file. This is the source folder, not + * the package folder. + * @param osResPath the OS path to the res folder for the main project + * @param osManifestPath the OS path to the manifest of the main project + * @param packageFolder the IFolder that will contain the generated file. Unlike + * osOutputPath this is the direct parent of the geenerated files. + * If customJavaPackage is not null, this must match the new destination triggered + * by its value. + * @param libResFolders the list of res folders for the library. + * @param customJavaPackage an optional javapackage to replace the main project java package. + * can be null. + * @throws CoreException + */ + private void execAapt(IProject project, IAndroidTarget projectTarget, String osOutputPath, + String osResPath, String osManifestPath, IFolder packageFolder, + ArrayList libResFolders, String customJavaPackage) throws CoreException { + // since the R.java file may be already existing in read-only + // mode we need to make it readable so that aapt can overwrite it + IFile rJavaFile = packageFolder.getFile(AndroidConstants.FN_RESOURCE_CLASS); + + // do the same for the Manifest.java class + IFile manifestJavaFile = packageFolder.getFile(AndroidConstants.FN_MANIFEST_CLASS); + + // we actually need to delete the manifest.java as it may become empty and + // in this case aapt doesn't generate an empty one, but instead doesn't + // touch it. + manifestJavaFile.delete(true, null); + + // launch aapt: create the command line + ArrayList array = new ArrayList(); + array.add(projectTarget.getPath(IAndroidTarget.AAPT)); + array.add("package"); //$NON-NLS-1$ + array.add("-m"); //$NON-NLS-1$ + if (AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE) { + array.add("-v"); //$NON-NLS-1$ + } + + if (libResFolders.size() > 0) { + array.add("--auto-add-overlay"); //$NON-NLS-1$ + } + + if (customJavaPackage != null) { + array.add("--custom-package"); //$NON-NLS-1$ + array.add(customJavaPackage); + } + + array.add("-J"); //$NON-NLS-1$ + array.add(osOutputPath); + array.add("-M"); //$NON-NLS-1$ + array.add(osManifestPath); + array.add("-S"); //$NON-NLS-1$ + array.add(osResPath); + for (IFolder libResFolder : libResFolders) { + array.add("-S"); //$NON-NLS-1$ + array.add(libResFolder.getLocation().toOSString()); + } + + array.add("-I"); //$NON-NLS-1$ + array.add(projectTarget.getPath(IAndroidTarget.ANDROID_JAR)); + + if (AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE) { + StringBuilder sb = new StringBuilder(); + for (String c : array) { + sb.append(c); + sb.append(' '); + } + String cmd_line = sb.toString(); + AdtPlugin.printToConsole(project, cmd_line); + } + + // launch + int execError = 1; + try { + // launch the command line process + Process process = Runtime.getRuntime().exec( + array.toArray(new String[array.size()])); + + // list to store each line of stderr + ArrayList results = new ArrayList(); + + // get the output and return code from the process + execError = grabProcessOutput(process, results); + + // attempt to parse the error output + boolean parsingError = parseAaptOutput(results, project); + + // if we couldn't parse the output we display it in the console. + if (parsingError) { + if (execError != 0) { + AdtPlugin.printErrorToConsole(project, results.toArray()); + } else { + AdtPlugin.printBuildToConsole(BuildVerbosity.NORMAL, + project, results.toArray()); + } + } + + if (execError != 0) { + // if the exec failed, and we couldn't parse the error output + // (and therefore not all files that should have been marked, + // were marked), we put a generic marker on the project and abort. + if (parsingError) { + markProject(AndroidConstants.MARKER_ADT, + Messages.Unparsed_AAPT_Errors, IMarker.SEVERITY_ERROR); + } + + AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, + Messages.AAPT_Error); + + // abort if exec failed. + // This interrupts the build. The next builders will not run. + stopBuild(Messages.AAPT_Error); + } + } catch (IOException e1) { + // something happen while executing the process, + // mark the project and exit + String msg = String.format(Messages.AAPT_Exec_Error, array.get(0)); + markProject(AndroidConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); + + // This interrupts the build. The next builders will not run. + stopBuild(msg); + } catch (InterruptedException e) { + // we got interrupted waiting for the process to end... + // mark the project and exit + String msg = String.format(Messages.AAPT_Exec_Error, array.get(0)); + markProject(AndroidConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); + + // This interrupts the build. The next builders will not run. + stopBuild(msg); + } + + // if the return code was OK, we refresh the folder that + // contains R.java to force a java recompile. + if (execError == 0) { + // now add the R.java/Manifest.java to the list of file to be marked + // as derived. + mDerivedProgressMonitor.addFile(rJavaFile); + mDerivedProgressMonitor.addFile(manifestJavaFile); + + // build has been done. reset the state of the builder + mMustCompileResources = false; + + // and store it + saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, + mMustCompileResources); + } + } + + /** * Delete the a generated java class associated with the specified java package. * @param filename Name of the generated file to remove. * @param javaPackage the old java package @@ -720,13 +808,11 @@ public class PreCompilerBuilder extends BaseBuilder { * Returns an {@link IFolder} (located inside the 'gen' source folder), that matches the * package defined in the manifest. This {@link IFolder} may not actually exist * (aapt will create it anyway). - * @param project The project. * @return the {@link IFolder} that will contain the R class or null if * the folder was not found. * @throws CoreException */ - private IFolder getGenManifestPackageFolder(IProject project) - throws CoreException { + private IFolder getGenManifestPackageFolder() throws CoreException { // get the path for the package IPath packagePath = getJavaPackagePath(mManifestPackage); @@ -736,6 +822,24 @@ public class PreCompilerBuilder extends BaseBuilder { } /** + * Returns an {@link IFolder} (located inside the 'gen' source folder), that matches the + * given package. This {@link IFolder} may not actually exist + * (aapt will create it anyway). + * @param javaPackage the java package that must match the folder. + * @return the {@link IFolder} that will contain the R class or null if + * the folder was not found. + * @throws CoreException + */ + private IFolder getGenManifestPackageFolder(String javaPackage) throws CoreException { + // get the path for the package + IPath packagePath = getJavaPackagePath(javaPackage); + + // get a folder for this path under the 'gen' source folder, and return it. + // This IFolder may not reference an actual existing folder. + return mGenFolder.getFolder(packagePath); + } + + /** * Compiles aidl files into java. This will also removes old java files * created from aidl files that are now gone. * @param projectTarget Target of the project 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 6f0b157..b03f041 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 @@ -158,9 +158,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit try { AdtPlugin plugin = AdtPlugin.getDefault(); - // get the lock object for project manipulation during SDK load. - Object lock = plugin.getSdkLockObject(); - synchronized (lock) { + synchronized (Sdk.getLock()) { boolean sdkIsLoaded = plugin.getSdkLoadStatus() == LoadStatus.LOADED; // check if the project has a valid target. @@ -499,6 +497,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit * @param projects the list of projects to check. */ public static void checkProjectsCache(ArrayList projects) { + Sdk currentSdk = Sdk.getCurrent(); int i = 0; projectLoop: while (i < projects.size()) { IJavaProject javaProject = projects.get(i); @@ -513,8 +512,13 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit continue; } + // 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. + currentSdk.loadTarget(Sdk.getProject(iProject)); + // get the target from the project and its paths - IAndroidTarget target = Sdk.getCurrent().getTarget(javaProject.getProject()); + IAndroidTarget target = currentSdk.getTarget(javaProject.getProject()); if (target == null) { // this is really not supposed to happen. This would mean there are cached paths, // but default.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/AndroidManifestParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidManifestParser.java index 189fb81..8bbd018 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidManifestParser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidManifestParser.java @@ -952,26 +952,4 @@ public class AndroidManifestParser { } return (IFile) r; } - - /** - * Given a fully qualified activity name (e.g. com.foo.test.MyClass) and given a project - * package base name (e.g. com.foo), returns the relative activity name that would be used - * the "name" attribute of an "activity" element. - * - * @param fullActivityName a fully qualified activity class name, e.g. "com.foo.test.MyClass" - * @param packageName The project base package name, e.g. "com.foo" - * @return The relative activity name if it can be computed or the original fullActivityName. - */ - public static String extractActivityName(String fullActivityName, String packageName) { - if (packageName != null && fullActivityName != null) { - if (packageName.length() > 0 && fullActivityName.startsWith(packageName)) { - String name = fullActivityName.substring(packageName.length()); - if (name.length() > 0 && name.charAt(0) == '.') { - return name; - } - } - } - - return fullActivityName; - } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectState.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectState.java index 70af6fd..63d3d2b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectState.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectState.java @@ -17,11 +17,16 @@ package com.android.ide.eclipse.adt.internal.project; import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.internal.project.ApkConfigurationHelper; import com.android.sdklib.internal.project.ApkSettings; import com.android.sdklib.internal.project.ProjectProperties; import org.eclipse.core.resources.IProject; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + /** * Centralized state for Android Eclipse project. *

This gives raw access to the properties (from default.properties), as well @@ -30,14 +35,61 @@ import org.eclipse.core.resources.IProject; */ public final class ProjectState { + public final class LibraryState { + private final String mRelativePath; + private IProject mProject; + private String mPath; + + private LibraryState(String relativePath) { + mRelativePath = relativePath; + } + + private void setProject(IProject project) { + mProject = project; + mPath = project.getLocation().toOSString(); + + updateLibraries(); + } + + public String getRelativePath() { + return mRelativePath; + } + + public IProject getProject() { + return mProject; + } + + public String getProjectLocation() { + return mPath; + } + } + private final IProject mProject; private final ProjectProperties mProperties; + private final ArrayList mLibraries = new ArrayList(); private IAndroidTarget mTarget; private ApkSettings mApkSettings; + private IProject[] mLibraryProjects; public ProjectState(IProject project, ProjectProperties properties) { mProject = project; mProperties = properties; + + // load the ApkSettings + mApkSettings = ApkConfigurationHelper.getSettings(properties); + + // load the libraries + int index = 1; + while (true) { + String propName = ProjectProperties.PROPERTY_LIB_REF + Integer.toString(index++); + String rootPath = mProperties.getProperty(propName); + + if (rootPath == null) { + break; + } + + mLibraries.add(new LibraryState(convertPath(rootPath))); + } } public IProject getProject() { @@ -83,4 +135,78 @@ public final class ProjectState { public ApkSettings getApkSettings() { return mApkSettings; } + + public IProject[] getLibraryProjects() { + return mLibraryProjects; + } + + /** + * Returns whether this is a library project. + */ + public boolean isLibrary() { + String value = mProperties.getProperty(ProjectProperties.PROPERTY_LIBRARY); + return value != null && Boolean.valueOf(value); + } + + /** + * Returns whether the project is missing some required libraries. + */ + public boolean isMissingLibraries() { + for (LibraryState state : mLibraries) { + if (state.getProject() == null) { + return true; + } + } + + return false; + } + + /** + * Returns whether a given library project is needed by the receiver. + * @param libraryProject the library project to check. + * @return a non null object if the project is a library dependency. + */ + public LibraryState needs(IProject libraryProject) { + // compute current location + File projectFile = new File(mProject.getLocation().toOSString()); + + // get the location of the library. + File libraryFile = new File(libraryProject.getLocation().toOSString()); + + // loop on all libraries and check if the path match + for (LibraryState state : mLibraries) { + if (state.getProject() == null) { + File library = new File(projectFile, state.getRelativePath()); + try { + File absPath = library.getCanonicalFile(); + if (absPath.equals(libraryFile)) { + state.setProject(libraryProject); + return state; + } + } catch (IOException e) { + // ignore this library + } + } + } + + return null; + } + + private void updateLibraries() { + ArrayList list = new ArrayList(); + for (LibraryState state : mLibraries) { + if (state.getProject() != null) { + list.add(state.getProject()); + } + } + + mLibraryProjects = list.toArray(new IProject[list.size()]); + } + + /** + * Converts a path containing only / by the proper platform separator. + */ + private String convertPath(String path) { + return path.replaceAll("/", File.separator); //$NON-NLS-1$ + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/MultiResourceFile.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/MultiResourceFile.java index 3386e17..047834d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/MultiResourceFile.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/MultiResourceFile.java @@ -16,13 +16,13 @@ package com.android.ide.eclipse.adt.internal.resources.manager; -import com.android.builders.IAbstractFile; -import com.android.builders.StreamException; import com.android.ide.eclipse.adt.internal.resources.ResourceType; import com.android.layoutlib.api.IResourceValue; import com.android.layoutlib.utils.ResourceValue; import com.android.layoutlib.utils.ValueResourceParser; import com.android.layoutlib.utils.ValueResourceParser.IValueResourceRepository; +import com.android.sdklib.internal.io.IAbstractFile; +import com.android.sdklib.internal.io.StreamException; import org.xml.sax.SAXException; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java index e91ebe5..cb05653 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java @@ -16,7 +16,6 @@ package com.android.ide.eclipse.adt.internal.resources.manager; -import com.android.builders.IAbstractFolder; import com.android.ide.eclipse.adt.internal.resources.IResourceRepository; import com.android.ide.eclipse.adt.internal.resources.ResourceItem; import com.android.ide.eclipse.adt.internal.resources.ResourceType; @@ -27,6 +26,7 @@ import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQua import com.android.ide.eclipse.adt.internal.resources.manager.files.IFolderWrapper; import com.android.layoutlib.api.IResourceValue; import com.android.layoutlib.utils.ResourceValue; +import com.android.sdklib.internal.io.IAbstractFolder; import org.eclipse.core.resources.IFolder; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFile.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFile.java index 75f002b..82f73f7 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFile.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFile.java @@ -16,10 +16,10 @@ package com.android.ide.eclipse.adt.internal.resources.manager; -import com.android.builders.IAbstractFile; import com.android.ide.eclipse.adt.internal.resources.ResourceType; import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration; import com.android.layoutlib.api.IResourceValue; +import com.android.sdklib.internal.io.IAbstractFile; import java.util.Collection; @@ -27,15 +27,15 @@ import java.util.Collection; * Represents a Resource file (a file under $Project/res/) */ public abstract class ResourceFile extends Resource { - + private final IAbstractFile mFile; private final ResourceFolder mFolder; - + protected ResourceFile(IAbstractFile file, ResourceFolder folder) { mFile = file; mFolder = folder; } - + /* * (non-Javadoc) * @see com.android.ide.eclipse.editors.resources.manager.Resource#getConfiguration() @@ -44,21 +44,21 @@ public abstract class ResourceFile extends Resource { public FolderConfiguration getConfiguration() { return mFolder.getConfiguration(); } - + /** * Returns the IFile associated with the ResourceFile. */ public final IAbstractFile getFile() { return mFile; } - + /** * Returns the parent folder as a {@link ResourceFolder}. */ public final ResourceFolder getFolder() { return mFolder; } - + /** * Returns whether the resource is a framework resource. */ @@ -88,10 +88,10 @@ public abstract class ResourceFile extends Resource { */ public abstract Collection getResources(ResourceType type, ProjectResources projectResources); - + /** * Returns the value of a resource generated by this file by {@link ResourceType} and name. - *

If no resource match, null is returned. + *

If no resource match, null is returned. * @param type the type of the resource. * @param name the name of the resource. */ diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFolder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFolder.java index ae32300..7cb6605 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFolder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFolder.java @@ -16,12 +16,12 @@ package com.android.ide.eclipse.adt.internal.resources.manager; -import com.android.builders.IAbstractFile; -import com.android.builders.IAbstractFolder; import com.android.ide.eclipse.adt.internal.resources.ResourceItem; import com.android.ide.eclipse.adt.internal.resources.ResourceType; import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration; import com.android.ide.eclipse.adt.internal.resources.manager.files.IFileWrapper; +import com.android.sdklib.internal.io.IAbstractFile; +import com.android.sdklib.internal.io.IAbstractFolder; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java index 162e163..6da34f4 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java @@ -16,10 +16,6 @@ package com.android.ide.eclipse.adt.internal.resources.manager; -import com.android.builders.FileWrapper; -import com.android.builders.FolderWrapper; -import com.android.builders.IAbstractFile; -import com.android.builders.IAbstractFolder; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.AndroidConstants; import com.android.ide.eclipse.adt.internal.resources.ResourceType; @@ -32,6 +28,10 @@ import com.android.ide.eclipse.adt.internal.resources.manager.files.IFileWrapper import com.android.ide.eclipse.adt.internal.resources.manager.files.IFolderWrapper; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkConstants; +import com.android.sdklib.internal.io.FileWrapper; +import com.android.sdklib.internal.io.FolderWrapper; +import com.android.sdklib.internal.io.IAbstractFile; +import com.android.sdklib.internal.io.IAbstractFolder; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/SingleResourceFile.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/SingleResourceFile.java index 8e688f6..8af3f53 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/SingleResourceFile.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/SingleResourceFile.java @@ -16,12 +16,12 @@ package com.android.ide.eclipse.adt.internal.resources.manager; -import com.android.builders.IAbstractFile; import com.android.ide.eclipse.adt.internal.resources.ResourceType; import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier; import com.android.layoutlib.api.IResourceValue; import com.android.layoutlib.utils.DensityBasedResourceValue; import com.android.layoutlib.utils.ResourceValue; +import com.android.sdklib.internal.io.IAbstractFile; import java.util.ArrayList; import java.util.Collection; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/files/IFileWrapper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/files/IFileWrapper.java index 27084c0..b8db6b0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/files/IFileWrapper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/files/IFileWrapper.java @@ -16,8 +16,8 @@ package com.android.ide.eclipse.adt.internal.resources.manager.files; -import com.android.builders.IAbstractFile; -import com.android.builders.StreamException; +import com.android.sdklib.internal.io.IAbstractFile; +import com.android.sdklib.internal.io.StreamException; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; @@ -51,6 +51,10 @@ public class IFileWrapper implements IAbstractFile { return mFile.getName(); } + public boolean exists() { + return mFile.exists(); + } + /** * Returns the {@link IFile} object that the receiver could represent. Can be null */ diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/files/IFolderWrapper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/files/IFolderWrapper.java index 4df4ccb..23dab60 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/files/IFolderWrapper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/files/IFolderWrapper.java @@ -16,30 +16,45 @@ package com.android.ide.eclipse.adt.internal.resources.manager.files; -import com.android.builders.IAbstractFolder; +import com.android.sdklib.internal.io.IAbstractFile; +import com.android.sdklib.internal.io.IAbstractFolder; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Path; /** - * An implementation of {@link IAbstractFolder} on top of an {@link IFolder} object. + * An implementation of {@link IAbstractFolder} on top of either an {@link IFolder} or an + * {@link IContainer} object. */ public class IFolderWrapper implements IAbstractFolder { - private final IFolder mFolder; + private final IFolder mFolder; // could be null. + private final IContainer mContainer; // never null. public IFolderWrapper(IFolder folder) { - mFolder = folder; + mContainer = mFolder = folder; + } + + public IFolderWrapper(IContainer container) { + mFolder = container instanceof IFolder ? (IFolder)container : null; + mContainer = container; } public String getName() { - return mFolder.getName(); + return mContainer.getName(); + } + + public boolean exists() { + return mContainer.exists(); } public boolean hasFile(String name) { try { - IResource[] files = mFolder.members(); + IResource[] files = mContainer.members(); for (IResource file : files) { if (name.equals(file.getName())) { return true; @@ -52,6 +67,16 @@ public class IFolderWrapper implements IAbstractFolder { return false; } + public IAbstractFile getFile(String name) { + if (mFolder != null) { + IFile file = mFolder.getFile(name); + return new IFileWrapper(file); + } + + IFile file = mContainer.getFile(new Path(name)); + return new IFileWrapper(file); + } + /** * Returns the {@link IFolder} object that the receiver could represent. * Can be null @@ -75,6 +100,6 @@ public class IFolderWrapper implements IAbstractFolder { @Override public int hashCode() { - return mFolder.hashCode(); + return mContainer.hashCode(); } } 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 ef1cfeb..e657329 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 @@ -21,6 +21,7 @@ import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.project.AndroidClasspathContainerInitializer; import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; import com.android.ide.eclipse.adt.internal.project.ProjectState; +import com.android.ide.eclipse.adt.internal.project.ProjectState.LibraryState; 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.resources.manager.GlobalProjectMonitor.IProjectListener; @@ -38,16 +39,23 @@ import com.android.sdklib.internal.project.ProjectProperties; import com.android.sdklib.internal.project.ProjectProperties.PropertyType; import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IMarkerDelta; +import org.eclipse.core.resources.IPathVariableManager; import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; @@ -71,6 +79,8 @@ import java.util.Map.Entry; * To get the list of platforms or add-ons present in the SDK, call {@link #getTargets()}. */ public class Sdk implements IProjectListener, IFileListener { + private final static Object sLock = new Object(); + private static Sdk sCurrentSdk = null; /** @@ -164,64 +174,76 @@ public class Sdk implements IProjectListener, IFileListener { } /** + * Returns the lock object used to synchronize all operations dealing with SDK, targets and + * projects. + */ + public static final Object getLock() { + return sLock; + } + + /** * Loads an SDK and returns an {@link Sdk} object if success. *

If the SDK failed to load, it displays an error to the user. * @param sdkLocation the OS path to the SDK. */ - public static synchronized Sdk loadSdk(String sdkLocation) { - if (sCurrentSdk != null) { - sCurrentSdk.dispose(); - sCurrentSdk = null; - } + public static Sdk loadSdk(String sdkLocation) { + synchronized (sLock) { + if (sCurrentSdk != null) { + sCurrentSdk.dispose(); + sCurrentSdk = null; + } - final ArrayList logMessages = new ArrayList(); - ISdkLog log = new ISdkLog() { - public void error(Throwable throwable, String errorFormat, Object... arg) { - if (errorFormat != null) { - logMessages.add(String.format("Error: " + errorFormat, arg)); - } + final ArrayList logMessages = new ArrayList(); + ISdkLog log = new ISdkLog() { + public void error(Throwable throwable, String errorFormat, Object... arg) { + if (errorFormat != null) { + logMessages.add(String.format("Error: " + errorFormat, arg)); + } - if (throwable != null) { - logMessages.add(throwable.getMessage()); + if (throwable != null) { + logMessages.add(throwable.getMessage()); + } } - } - public void warning(String warningFormat, Object... arg) { - logMessages.add(String.format("Warning: " + warningFormat, arg)); - } + public void warning(String warningFormat, Object... arg) { + logMessages.add(String.format("Warning: " + warningFormat, arg)); + } - public void printf(String msgFormat, Object... arg) { - logMessages.add(String.format(msgFormat, arg)); - } - }; + public void printf(String msgFormat, Object... arg) { + logMessages.add(String.format(msgFormat, arg)); + } + }; - // get an SdkManager object for the location - SdkManager manager = SdkManager.createManager(sdkLocation, log); - if (manager != null) { - AvdManager avdManager = null; - try { - avdManager = new AvdManager(manager, log); - } catch (AndroidLocationException e) { - log.error(e, "Error parsing the AVDs"); - } - sCurrentSdk = new Sdk(manager, avdManager); - return sCurrentSdk; - } else { - StringBuilder sb = new StringBuilder("Error Loading the SDK:\n"); - for (String msg : logMessages) { - sb.append('\n'); - sb.append(msg); + // get an SdkManager object for the location + SdkManager manager = SdkManager.createManager(sdkLocation, log); + if (manager != null) { + AvdManager avdManager = null; + try { + avdManager = new AvdManager(manager, log); + } catch (AndroidLocationException e) { + log.error(e, "Error parsing the AVDs"); + } + sCurrentSdk = new Sdk(manager, avdManager); + return sCurrentSdk; + } else { + StringBuilder sb = new StringBuilder("Error Loading the SDK:\n"); + for (String msg : logMessages) { + sb.append('\n'); + sb.append(msg); + } + AdtPlugin.displayError("Android SDK", sb.toString()); } - AdtPlugin.displayError("Android SDK", sb.toString()); + return null; } - return null; } /** * Returns the current {@link Sdk} object. */ - public static synchronized Sdk getCurrent() { - return sCurrentSdk; + public static Sdk getCurrent() { + synchronized (sLock) { + return sCurrentSdk; + } } /** @@ -273,7 +295,7 @@ public class Sdk implements IProjectListener, IFileListener { } - synchronized (AdtPlugin.getDefault().getSdkLockObject()) { + synchronized (sLock) { boolean resolveProject = false; ProjectState state = getProject(project); @@ -355,7 +377,7 @@ public class Sdk implements IProjectListener, IFileListener { return null; } - synchronized (AdtPlugin.getDefault().getSdkLockObject()) { + synchronized (sLock) { ProjectState state = sProjectStateMap.get(project); if (state == null) { // load the default.properties from the project folder. @@ -376,9 +398,10 @@ public class Sdk implements IProjectListener, IFileListener { state = new ProjectState(project, properties); sProjectStateMap.put(project, state); - // load the ApkSettings as well. - ApkSettings apkSettings = ApkConfigurationHelper.getSettings(properties); - state.setApkSettings(apkSettings); + // try to resolve the target + if (AdtPlugin.getDefault().getSdkLoadStatus() == LoadStatus.LOADED) { + sCurrentSdk.loadTarget(state); + } } return state; @@ -402,6 +425,19 @@ public class Sdk implements IProjectListener, IFileListener { } /** + * Loads the {@link IAndroidTarget} for a given project. + *

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. + */ + public void loadTarget(ProjectState state) { + String hash = state.getTargetHashString(); + if (hash != null) { + state.setTarget(getTargetFromHashString(hash)); + } + } + + /** * Checks and loads (if needed) the data for a given target. *

The data is loaded in a separate {@link Job}, and opened editors will be notified * through their implementation of {@link ITargetChangeListener#onTargetLoaded(IAndroidTarget)}. @@ -417,7 +453,7 @@ public class Sdk implements IProjectListener, IFileListener { public LoadStatus checkAndLoadTargetData(final IAndroidTarget target, IJavaProject project) { boolean loadData = false; - synchronized (AdtPlugin.getDefault().getSdkLockObject()) { + synchronized (sLock) { TargetLoadBundle bundle = mTargetDataStatusMap.get(target); if (bundle == null) { bundle = new TargetLoadBundle(); @@ -455,7 +491,7 @@ public class Sdk implements IProjectListener, IFileListener { IJavaProject[] javaProjectArray = null; - synchronized (plugin.getSdkLockObject()) { + synchronized (sLock) { TargetLoadBundle bundle = mTargetDataStatusMap.get(target); if (status.getCode() != IStatus.OK) { @@ -480,7 +516,7 @@ public class Sdk implements IProjectListener, IFileListener { return status; } catch (Throwable t) { - synchronized (plugin.getSdkLockObject()) { + synchronized (sLock) { TargetLoadBundle bundle = mTargetDataStatusMap.get(target); bundle.status = LoadStatus.FAILED; } @@ -507,7 +543,7 @@ public class Sdk implements IProjectListener, IFileListener { * Return the {@link AndroidTargetData} for a given {@link IAndroidTarget}. */ public AndroidTargetData getTargetData(IAndroidTarget target) { - synchronized (AdtPlugin.getDefault().getSdkLockObject()) { + synchronized (sLock) { return mTargetDataMap.get(target); } } @@ -516,7 +552,7 @@ public class Sdk implements IProjectListener, IFileListener { * Return the {@link AndroidTargetData} for a given {@link IProject}. */ public AndroidTargetData getTargetData(IProject project) { - synchronized (AdtPlugin.getDefault().getSdkLockObject()) { + synchronized (sLock) { IAndroidTarget target = getTarget(project); if (target != null) { return getTargetData(target); @@ -572,7 +608,7 @@ public class Sdk implements IProjectListener, IFileListener { loadLayoutDevices(); // update whatever ProjectState is already present with new IAndroidTarget objects. - synchronized (AdtPlugin.getDefault().getSdkLockObject()) { + synchronized (sLock) { for (Entry entry: sProjectStateMap.entrySet()) { entry.getValue().setTarget( getTargetFromHashString(entry.getValue().getTargetHashString())); @@ -589,7 +625,7 @@ public class Sdk implements IProjectListener, IFileListener { monitor.removeFileListener(this); // the IAndroidTarget objects are now obsolete so update the project states. - synchronized (AdtPlugin.getDefault().getSdkLockObject()) { + synchronized (sLock) { for (Entry entry: sProjectStateMap.entrySet()) { entry.getValue().setTarget(null); } @@ -597,7 +633,7 @@ public class Sdk implements IProjectListener, IFileListener { } void setTargetData(IAndroidTarget target, AndroidTargetData data) { - synchronized (AdtPlugin.getDefault().getSdkLockObject()) { + synchronized (sLock) { mTargetDataMap.put(target, data); } } @@ -659,7 +695,7 @@ public class Sdk implements IProjectListener, IFileListener { public void projectClosed(IProject project) { // get the target project - synchronized (AdtPlugin.getDefault().getSdkLockObject()) { + synchronized (sLock) { // direct access to the map since we're going to edit it. ProjectState state = sProjectStateMap.get(project); if (state != null) { @@ -685,14 +721,33 @@ public class Sdk implements IProjectListener, IFileListener { projectClosed(project); } - public void projectOpened(final IProject project) { - // ignore this. The project will be added to the map the first time the target needs - // to be resolved. + public void projectOpened(IProject openedProject) { + ProjectState openedState = getProject(openedProject); + if (openedState != null) { + // find dependencies, if any + if (openedState.isMissingLibraries()) { + // look for all opened projects to see if they are valid library for this project. + } + + // if the project is a library, then try to see if it's required by other projects. + if (openedState.isLibrary()) { + setupLibraryProject(openedProject); + + synchronized (sLock) { + for (ProjectState projectState : sProjectStateMap.values()) { + if (projectState != openedState && projectState.isMissingLibraries()) { + LibraryState libState = projectState.needs(openedProject); + if (libState != null) { + linkProjectAndLibrary(projectState, libState); + } + } + } + } + } + } } public void projectOpenedWithWorkspace(IProject project) { - // ignore this. The project will be added to the map the first time the target needs - // to be resolved. projectOpened(project); } @@ -723,5 +778,84 @@ public class Sdk implements IProjectListener, IFileListener { job.schedule(); } } + + private void setupLibraryProject(IProject libProject) { + // if needed add a path var for this library + IPathVariableManager pathVarMgr = + ResourcesPlugin.getWorkspace().getPathVariableManager(); + IPath libPath = libProject.getLocation(); + + final String libName = libProject.getName(); + final String varName = "_android_" + libName; //$NON-NLS-1$ + + if (libPath.equals(pathVarMgr.getValue(varName)) == false) { + try { + pathVarMgr.setValue(varName, libPath); + } catch (CoreException e) { + String message = String.format("Unable to set linked path var '%1$s' for library %2$s", + varName, libPath.toOSString()); + AdtPlugin.log(e, message); + } + } + } + + /** + * Links a project and a library so that the project can use the library code and resources. + *

This is done in a job to be sure that the workspace is not locked for resource + * modification. + * @param projectState the {@link ProjectState} for the main project + * @param libraryState the {@link LibraryState} for the library project. + */ + private void linkProjectAndLibrary(final ProjectState projectState, + final LibraryState libraryState) { + Job job = new Job("Android Library link creation") { //$NON-NLS-1$ + @Override + protected IStatus run(IProgressMonitor monitor) { + try { + IProject project = projectState.getProject(); + IProject library = libraryState.getProject(); + + // add the library to the list of dynamic references + IProjectDescription projectDescription = project.getDescription(); + IProject[] refs = projectDescription.getDynamicReferences(); + if (refs.length > 0) { + IProject[] newrefs = new IProject[refs.length + 1]; + System.arraycopy(refs, 0, newrefs, 0, refs.length); + newrefs[refs.length] = library; + refs = newrefs; + } else { + refs = new IProject[] { library }; + } + projectDescription.setDynamicReferences(refs); + + // add a linked resource for the source of the library and add it to the project + final String libName = library.getName(); + final String varName = "_android_" + libName; //$NON-NLS-1$ + + // create a linked resource for the library using the path var. + IFolder libSrc = project.getFolder(libName); + // FIXME: make sure src has not been overriden? + String libSrcFolder = "src"; //$NON-NLS-1$ + libSrc.createLink(new Path(varName + "/" + libSrcFolder), //$NON-NLS-1$ + IResource.REPLACE, monitor); + + // use the folder as a source folder + IJavaProject javaProject = JavaCore.create(project); + IClasspathEntry[] entries = javaProject.getRawClasspath(); + + IClasspathEntry[] newEntries = new IClasspathEntry[entries.length + 1]; + System.arraycopy(entries, 0, newEntries, 0, entries.length); + newEntries[entries.length] = JavaCore.newSourceEntry(libSrc.getFullPath()); + javaProject.setRawClasspath(newEntries, monitor); + + return Status.OK_STATUS; + } catch (CoreException e) { + return e.getStatus(); + } + } + }; + job.setPriority(Job.BUILD); + job.schedule(); + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceExplorerView.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceExplorerView.java index 77c67ef..ddf3f36 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceExplorerView.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceExplorerView.java @@ -16,7 +16,6 @@ package com.android.ide.eclipse.adt.internal.ui; -import com.android.builders.IAbstractFile; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.AndroidConstants; import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResourceItem; @@ -25,6 +24,7 @@ import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile; import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IResourceEventListener; import com.android.ide.eclipse.adt.internal.resources.manager.files.IFileWrapper; +import com.android.sdklib.internal.io.IAbstractFile; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java index 733d60c..78346c1 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java @@ -33,6 +33,7 @@ import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkConstants; import com.android.sdklib.internal.project.ProjectProperties; import com.android.sdklib.internal.project.ProjectProperties.PropertyType; +import com.android.sdklib.xml.AndroidManifest; import com.android.sdkuilib.internal.widgets.SdkTargetSelector; import org.eclipse.core.filesystem.URIUtil; @@ -1052,7 +1053,7 @@ public class NewProjectCreationPage extends WizardPage { } if (activity != null) { - activityName = AndroidManifestParser.extractActivityName(activity.getName(), + activityName = AndroidManifest.extractActivityName(activity.getName(), packageName); } diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/.classpath b/eclipse/plugins/com.android.ide.eclipse.tests/.classpath index 17d578d..660adf3 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/.classpath +++ b/eclipse/plugins/com.android.ide.eclipse.tests/.classpath @@ -11,6 +11,5 @@ - diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkTestCase.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkTestCase.java index d3210eb..9862ba2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkTestCase.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkTestCase.java @@ -60,7 +60,7 @@ public abstract class SdkTestCase extends TestCase { return null; } - Object sdkLock = adt.getSdkLockObject(); + Object sdkLock = Sdk.getLock(); LoadStatus loadStatus = LoadStatus.LOADING; // wait for ADT to load the SDK on a separate thread // loop max of 600 times * 200 ms = 2 minutes diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/manager/ConfigMatchTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/manager/ConfigMatchTest.java index e81e70e..a09b82f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/manager/ConfigMatchTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/manager/ConfigMatchTest.java @@ -16,7 +16,6 @@ package com.android.ide.eclipse.adt.internal.editors.resources.manager; -import com.android.builders.IAbstractFolder; import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration; import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQualifier; import com.android.ide.eclipse.adt.internal.resources.configurations.KeyboardStateQualifier.KeyboardState; @@ -35,6 +34,7 @@ import com.android.ide.eclipse.adt.internal.resources.manager.files.IFolderWrapp import com.android.ide.eclipse.mock.FileMock; import com.android.ide.eclipse.mock.FolderMock; import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.internal.io.IAbstractFolder; import java.lang.reflect.Field; import java.lang.reflect.Method; diff --git a/eclipse/scripts/create_adt_symlinks.sh b/eclipse/scripts/create_adt_symlinks.sh index 95f0325..46d0d33 100755 --- a/eclipse/scripts/create_adt_symlinks.sh +++ b/eclipse/scripts/create_adt_symlinks.sh @@ -14,7 +14,7 @@ DEST="sdk/eclipse/plugins/com.android.ide.eclipse.adt" # computes "../.." from DEST to here (in /android) BACK=`echo $DEST | sed 's@[^/]*@..@g'` -LIBS="sdkstats jarutils androidprefs layoutlib_api layoutlib_utils ninepatch sdklib sdkuilib builders" +LIBS="sdkstats jarutils androidprefs layoutlib_api layoutlib_utils ninepatch sdklib sdkuilib" echo "make java libs ..." make -j3 showcommands $LIBS || die "ADT: Fail to build one of $LIBS." diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/FileWrapper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/FileWrapper.java new file mode 100644 index 0000000..b8af4be --- /dev/null +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/FileWrapper.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdklib.internal.io; + + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; + +/** + * An implementation of {@link IAbstractFile} on top of a {@link File} object. + * + */ +public class FileWrapper implements IAbstractFile { + + private final File mFile; + + /** + * Constructs a {@link FileWrapper} object. The underlying {@link File} object needs not + * exist or be a valid file. + */ + public FileWrapper(File file) { + mFile = file; + } + + public InputStream getContents() throws StreamException { + try { + return new FileInputStream(mFile); + } catch (FileNotFoundException e) { + throw new StreamException(e); + } + } + + public String getOsLocation() { + return mFile.getAbsolutePath(); + } + + public String getName() { + return mFile.getName(); + } + + public boolean exists() { + return mFile.isFile(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof FileWrapper) { + return mFile.equals(((FileWrapper)obj).mFile); + } + + if (obj instanceof File) { + return mFile.equals(obj); + } + + return super.equals(obj); + } + + @Override + public int hashCode() { + return mFile.hashCode(); + } +} diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/FolderWrapper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/FolderWrapper.java new file mode 100644 index 0000000..a9269ad --- /dev/null +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/FolderWrapper.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdklib.internal.io; + + +import java.io.File; + +/** + * An implementation of {@link IAbstractFolder} on top of a {@link File} object. + */ +public class FolderWrapper implements IAbstractFolder { + + private final File mFolder; + + /** + * Constructs a {@link FileWrapper} object. The underlying {@link File} object needs not exists + * or be a valid directory. + */ + public FolderWrapper(File folder) { + mFolder = folder; + } + + public boolean hasFile(String name) { + return false; + } + + public IAbstractFile getFile(String name) { + return new FileWrapper(new File(mFolder, name)); + } + + public String getName() { + return mFolder.getName(); + } + + public boolean exists() { + return mFolder.isDirectory(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof FolderWrapper) { + return mFolder.equals(((FolderWrapper)obj).mFolder); + } + + if (obj instanceof File) { + return mFolder.equals(obj); + } + + return super.equals(obj); + } + + @Override + public int hashCode() { + return mFolder.hashCode(); + } +} diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/IAbstractFile.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/IAbstractFile.java new file mode 100644 index 0000000..f96ede6 --- /dev/null +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/IAbstractFile.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdklib.internal.io; + +import java.io.InputStream; + +/** + * A file. + */ +public interface IAbstractFile extends IAbstractResource { + + /** + * Returns an {@link InputStream} object on the file content. + * @throws CoreException + */ + InputStream getContents() throws StreamException; + + /** + * Returns the OS path of the file location. + */ + String getOsLocation(); +} diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/IAbstractFolder.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/IAbstractFolder.java new file mode 100644 index 0000000..22f654b --- /dev/null +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/IAbstractFolder.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdklib.internal.io; + + +/** + * A folder. + */ +public interface IAbstractFolder extends IAbstractResource { + + /** + * Returns true if the receiver contains a file with a given name + * @param name the name of the file. This is the name without the path leading to the + * parent folder. + */ + boolean hasFile(String name); + + /** + * returns an {@link IAbstractFile} representing a child of the current folder with the + * given name. The file may not actually exist. + * @param name the name of the file. + */ + IAbstractFile getFile(String name); +} diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/IAbstractResource.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/IAbstractResource.java new file mode 100644 index 0000000..b34a404 --- /dev/null +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/IAbstractResource.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdklib.internal.io; + +/** + * Base representation of a file system resource.

+ * This somewhat limited interface is designed to let classes use file-system resources, without + * having the manually handle either the standard Java file or the Eclipse file API.. + */ +public interface IAbstractResource { + + /** + * Returns the name of the resource. + */ + String getName(); + + /** + * Returns whether the resource actually exists. + */ + boolean exists(); +} diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/StreamException.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/StreamException.java new file mode 100644 index 0000000..70b1c8e --- /dev/null +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/StreamException.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdklib.internal.io; + +/** + * Exception thrown when {@link IAbstractFile#getContents()} fails. + */ +public class StreamException extends Exception { + private static final long serialVersionUID = 1L; + + public StreamException(Exception e) { + super(e); + } +} diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifest.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifest.java index c4fa8bc..ae61a32 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifest.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifest.java @@ -16,6 +16,16 @@ package com.android.sdklib.xml; +import com.android.sdklib.SdkConstants; +import com.android.sdklib.internal.io.IAbstractFile; +import com.android.sdklib.internal.io.IAbstractFolder; +import com.android.sdklib.internal.io.StreamException; + +import org.xml.sax.InputSource; + +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpressionException; + /** * Helper and Constants for the AndroidManifest.xml file. * @@ -43,6 +53,21 @@ public final class AndroidManifest { public final static String ATTRIBUTE_TARGET_PACKAGE = "targetPackage"; //$NON-NLS-1$ public final static String ATTRIBUTE_EXPORTED = "exported"; //$NON-NLS-1$ + public static String getPackage(IAbstractFolder projectFolder) + throws XPathExpressionException, StreamException { + IAbstractFile file = projectFolder.getFile(SdkConstants.FN_ANDROID_MANIFEST_XML); + return getPackage(file); + } + + public static String getPackage(IAbstractFile manifestFile) + throws XPathExpressionException, StreamException { + XPath xPath = AndroidXPathFactory.newXPath(); + + return xPath.evaluate( + "/" + NODE_MANIFEST + + "/@" + ATTRIBUTE_PACKAGE, + new InputSource(manifestFile.getContents())); + } /** * Combines a java package, with a class value from the manifest to make a fully qualified @@ -77,4 +102,25 @@ public final class AndroidManifest { } } + /** + * Given a fully qualified activity name (e.g. com.foo.test.MyClass) and given a project + * package base name (e.g. com.foo), returns the relative activity name that would be used + * the "name" attribute of an "activity" element. + * + * @param fullActivityName a fully qualified activity class name, e.g. "com.foo.test.MyClass" + * @param packageName The project base package name, e.g. "com.foo" + * @return The relative activity name if it can be computed or the original fullActivityName. + */ + public static String extractActivityName(String fullActivityName, String packageName) { + if (packageName != null && fullActivityName != null) { + if (packageName.length() > 0 && fullActivityName.startsWith(packageName)) { + String name = fullActivityName.substring(packageName.length()); + if (name.length() > 0 && name.charAt(0) == '.') { + return name; + } + } + } + + return fullActivityName; + } } -- cgit v1.1