diff options
author | Xavier Ducrohet <xav@android.com> | 2010-02-18 19:52:14 -0800 |
---|---|---|
committer | Xavier Ducrohet <xav@android.com> | 2010-02-23 16:18:21 -0800 |
commit | 610a7584cd2ede40772dbe95bd59e525a3859837 (patch) | |
tree | 01b03dcafc9aca050b7eba98933edac12eb6afea | |
parent | e87bc60813f90110c1156b086138e5e856c93809 (diff) | |
download | sdk-610a7584cd2ede40772dbe95bd59e525a3859837.zip sdk-610a7584cd2ede40772dbe95bd59e525a3859837.tar.gz sdk-610a7584cd2ede40772dbe95bd59e525a3859837.tar.bz2 |
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_<library name>.
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
36 files changed, 753 insertions, 398 deletions
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 @@ -<?xml version="1.0" encoding="UTF-8"?> -<classpath> - <classpathentry kind="src" path="src"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry kind="output" path="bin"/> -</classpath> 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 @@ -<?xml version="1.0" encoding="UTF-8"?> -<projectDescription> - <name>builders</name> - <comment></comment> - <projects> - </projects> - <buildSpec> - <buildCommand> - <name>org.eclipse.jdt.core.javabuilder</name> - <arguments> - </arguments> - </buildCommand> - </buildSpec> - <natures> - <nature>org.eclipse.jdt.core.javanature</nature> - </natures> -</projectDescription> 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/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 @@ <classpathentry kind="lib" path="sdkuilib.jar" sourcepath="/SdkUiLib"/> <classpathentry kind="lib" path="commons-compress-1.0.jar"/> <classpathentry kind="lib" path="groovy-all-1.7.0.jar" sourcepath="/GroovySrc/groovy-src-1.7.0.zip"/> - <classpathentry kind="lib" path="builders.jar"/> <classpathentry kind="output" path="bin"/> </classpath> 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<IJavaProject> list = new ArrayList<IJavaProject>(); - 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<IPath> 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<String> array = new ArrayList<String>(); - 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<String> results = new ArrayList<String>(); - - // 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<IFolder> libResFolders = new ArrayList<IFolder>(); + ArrayList<IFolder> libOutputFolders = new ArrayList<IFolder>(); + ArrayList<String> libJavaPackages = new ArrayList<String>(); + 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 + * <var>osOutputPath</var> this is the direct parent of the geenerated files. + * If <var>customJavaPackage</var> 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<IFolder> 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<String> array = new ArrayList<String>(); + 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<String> results = new ArrayList<String>(); + + // 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<IJavaProject> 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. * <p>This gives raw access to the properties (from <code>default.properties</code>), 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<LibraryState> mLibraries = new ArrayList<LibraryState>(); 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<IProject> list = new ArrayList<IProject>(); + 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<ProjectResourceItem> getResources(ResourceType type, ProjectResources projectResources); - + /** * Returns the value of a resource generated by this file by {@link ResourceType} and name. - * <p/>If no resource match, <code>null</code> is returned. + * <p/>If no resource match, <code>null</code> 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 <code>null</code> */ 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 <code>null</code> @@ -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. * <p/>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<String> logMessages = new ArrayList<String>(); - 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<String> logMessages = new ArrayList<String>(); + 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. + * <p/>This method will get the target hash string from the project properties, and resolve + * it to an {@link IAndroidTarget} object and store it inside the {@link ProjectState}. + * @param state the state representing the project to load. + */ + 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. * <p/> 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<IProject, ProjectState> 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<IProject, ProjectState> 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. + * <p/>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 @@ <classpathentry kind="lib" path="layoutlib.jar"/> <classpathentry kind="lib" path="kxml2-2.3.0.jar"/> <classpathentry kind="lib" path="groovy-all-1.7.0.jar"/> - <classpathentry kind="lib" path="/adt/builders.jar"/> <classpathentry kind="output" path="bin"/> </classpath> 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/builders/src/com/android/builders/FileWrapper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/FileWrapper.java index a564cae..b8af4be 100644 --- a/builders/src/com/android/builders/FileWrapper.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/FileWrapper.java @@ -14,13 +14,12 @@ * limitations under the License. */ -package com.android.builders; +package com.android.sdklib.internal.io; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.IOException; import java.io.InputStream; /** @@ -32,14 +31,10 @@ public class FileWrapper implements IAbstractFile { private final File mFile; /** - * Constructs a {@link FileWrapper} object. If {@link File#isFile()} returns <code>false</code> - * then an {@link IOException} is thrown. + * Constructs a {@link FileWrapper} object. The underlying {@link File} object needs not + * exist or be a valid file. */ - 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$ - } - + public FileWrapper(File file) { mFile = file; } @@ -59,6 +54,10 @@ public class FileWrapper implements IAbstractFile { return mFile.getName(); } + public boolean exists() { + return mFile.isFile(); + } + @Override public boolean equals(Object obj) { if (obj instanceof FileWrapper) { diff --git a/builders/src/com/android/builders/FolderWrapper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/FolderWrapper.java index 6d092c7..a9269ad 100644 --- a/builders/src/com/android/builders/FolderWrapper.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/FolderWrapper.java @@ -14,11 +14,10 @@ * limitations under the License. */ -package com.android.builders; +package com.android.sdklib.internal.io; import java.io.File; -import java.io.IOException; /** * An implementation of {@link IAbstractFolder} on top of a {@link File} object. @@ -28,14 +27,10 @@ public class FolderWrapper implements IAbstractFolder { private final File mFolder; /** - * Constructs a {@link FileWrapper} object. If {@link File#isDirectory()} returns - * <code>false</code> then an {@link IOException} is thrown. + * Constructs a {@link FileWrapper} object. The underlying {@link File} object needs not exists + * or be a valid directory. */ - 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$ - } - + public FolderWrapper(File folder) { mFolder = folder; } @@ -43,10 +38,18 @@ public class FolderWrapper implements IAbstractFolder { 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) { @@ -64,5 +67,4 @@ public class FolderWrapper implements IAbstractFolder { public int hashCode() { return mFolder.hashCode(); } - } diff --git a/builders/src/com/android/builders/IAbstractFile.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/IAbstractFile.java index 008d4b1..f96ede6 100644 --- a/builders/src/com/android/builders/IAbstractFile.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/IAbstractFile.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.builders; +package com.android.sdklib.internal.io; import java.io.InputStream; diff --git a/builders/src/com/android/builders/IAbstractFolder.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/IAbstractFolder.java index 92407ae..22f654b 100644 --- a/builders/src/com/android/builders/IAbstractFolder.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/IAbstractFolder.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.builders; +package com.android.sdklib.internal.io; /** @@ -28,4 +28,11 @@ public interface IAbstractFolder extends IAbstractResource { * 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/builders/src/com/android/builders/IAbstractResource.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/IAbstractResource.java index e573f84..b34a404 100644 --- a/builders/src/com/android/builders/IAbstractResource.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/IAbstractResource.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.builders; +package com.android.sdklib.internal.io; /** * Base representation of a file system resource.<p/> @@ -27,4 +27,9 @@ public interface IAbstractResource { * Returns the name of the resource. */ String getName(); + + /** + * Returns whether the resource actually exists. + */ + boolean exists(); } diff --git a/builders/src/com/android/builders/StreamException.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/StreamException.java index 4db2df3..70b1c8e 100644 --- a/builders/src/com/android/builders/StreamException.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/io/StreamException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.builders; +package com.android.sdklib.internal.io; /** * Exception thrown when {@link IAbstractFile#getContents()} fails. 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; + } } |