aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXavier Ducrohet <xav@android.com>2011-02-14 15:52:12 -0800
committerXavier Ducrohet <xav@android.com>2011-08-17 16:09:14 -0700
commitbabf25ca7162849f0145f75be1b6c09b359b122c (patch)
treec03538b2c6bc29e507b42d35cfe4642a804fed93
parent168e27a3d37e983726a82e66950f03926e7ea52f (diff)
downloadsdk-babf25ca7162849f0145f75be1b6c09b359b122c.zip
sdk-babf25ca7162849f0145f75be1b6c09b359b122c.tar.gz
sdk-babf25ca7162849f0145f75be1b6c09b359b122c.tar.bz2
New library project mechanism.
Libraries now generate a jar file that is used by the projects referencing them, instead of having the main projects compile the library source code themselves. This means we can remove the link mechanism that created linked folder in ADT and instead use a container that is lazily initalized to be all the jar files of the libraries. Also merged all 3 Ant build files (main_rules, lib_rules, test_rules) into a single build.xml that can build any kind of projects. Lot's of improvement in there too. Much cleaner. Change-Id: I98307e25cd76722e8595938528e6ef57a7e226ad
-rw-r--r--anttasks/src/com/android/ant/AaptExecLoopTask.java30
-rw-r--r--anttasks/src/com/android/ant/AidlExecTask.java5
-rw-r--r--anttasks/src/com/android/ant/AntConstants.java63
-rw-r--r--anttasks/src/com/android/ant/BaseTask.java39
-rw-r--r--anttasks/src/com/android/ant/MultiApkExportTask.java18
-rw-r--r--anttasks/src/com/android/ant/NewSetupTask.java638
-rw-r--r--anttasks/src/com/android/ant/SetupTask.java587
-rw-r--r--changes.txt28
-rw-r--r--eclipse/changes.txt6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java169
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java173
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java20
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutReloadMonitor.java8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainer.java21
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java43
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerPage.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/FolderDecorator.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/LibraryClasspathContainerInitializer.java122
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java22
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/GlobalProjectMonitor.java43
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java26
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/ProjectState.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java938
-rw-r--r--files/ant/lib_rules.xml186
-rw-r--r--files/ant/main_rules.xml1187
-rw-r--r--files/ant/pre_setup.xml10
-rw-r--r--files/ant/test_rules.xml106
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java8
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java2
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/build/ApkBuilder.java2
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/build/IArchiveBuilder.java35
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java25
-rw-r--r--templates/build.template41
-rw-r--r--testapps/basicLib/.classpath1
-rw-r--r--testapps/basicLibWithJar/.classpath1
-rw-r--r--testapps/basicProject/.classpath1
-rw-r--r--testapps/basicProjectWithAidl/.classpath1
-rw-r--r--testapps/basicProjectWithJar/.classpath1
-rw-r--r--testapps/basicProjectWithJava/.classpath1
-rw-r--r--testapps/basicProjectWithJavaFolder/.classpath1
-rw-r--r--testapps/basicProjectWithJavaFolder/.project2
-rw-r--r--testapps/basicProjectWithLib/.classpath3
-rw-r--r--testapps/basicProjectWithLib/.project12
46 files changed, 2322 insertions, 2331 deletions
diff --git a/anttasks/src/com/android/ant/AaptExecLoopTask.java b/anttasks/src/com/android/ant/AaptExecLoopTask.java
index ebefde5..6b438bb 100644
--- a/anttasks/src/com/android/ant/AaptExecLoopTask.java
+++ b/anttasks/src/com/android/ant/AaptExecLoopTask.java
@@ -88,6 +88,8 @@ public final class AaptExecLoopTask extends BaseTask {
private String mResourceFilter;
private String mRFolder;
private final ArrayList<NoCompress> mNoCompressList = new ArrayList<NoCompress>();
+ private String mProjectLibrariesResName;
+ private String mProjectLibrariesPackageName;
/**
* Sets the value of the "executable" attribute.
@@ -235,6 +237,15 @@ public final class AaptExecLoopTask extends BaseTask {
}
}
+ public void setProjectLibrariesResName(String projectLibrariesResName) {
+ mProjectLibrariesResName = projectLibrariesResName;
+ }
+
+ public void setProjectLibrariesPackageName(String projectLibrariesPackageName) {
+ mProjectLibrariesPackageName = projectLibrariesPackageName;
+ }
+
+
/**
* Returns an object representing a nested <var>nocompress</var> element.
*/
@@ -268,6 +279,13 @@ public final class AaptExecLoopTask extends BaseTask {
*/
@Override
public void execute() throws BuildException {
+ if (mProjectLibrariesResName == null) {
+ throw new BuildException("Missing attribute projectLibrariesResName");
+ }
+ if (mProjectLibrariesPackageName == null) {
+ throw new BuildException("Missing attribute projectLibrariesPackageName");
+ }
+
Project taskProject = getProject();
String libPkgProp = null;
@@ -275,7 +293,7 @@ public final class AaptExecLoopTask extends BaseTask {
// if the parameters indicate generation of the R class, check if
// more R classes need to be created for libraries.
if (mRFolder != null && new File(mRFolder).isDirectory()) {
- libPkgProp = taskProject.getProperty(AntConstants.PROP_PROJECT_LIBS_PKG);
+ libPkgProp = taskProject.getProperty(mProjectLibrariesPackageName);
if (libPkgProp != null) {
// Replace ";" with ":" since that's what aapt expects
libPkgProp = libPkgProp.replace(';', ':');
@@ -285,6 +303,11 @@ public final class AaptExecLoopTask extends BaseTask {
callAapt(libPkgProp);
}
+ @Override
+ protected String getExecTaskName() {
+ return "aapt";
+ }
+
/**
* Calls aapt with the given parameters.
* @param resourceFilter the resource configuration filter to pass to aapt (if configName is
@@ -298,7 +321,7 @@ public final class AaptExecLoopTask extends BaseTask {
final boolean generateRClass = mRFolder != null && new File(mRFolder).isDirectory();
// Get whether we have libraries
- Object libResRef = taskProject.getReference(AntConstants.PROP_PROJECT_LIBS_RES_REF);
+ Object libResRef = taskProject.getReference(mProjectLibrariesResName);
// Set up our folders to check for changed files
ArrayList<File> watchPaths = new ArrayList<File>();
@@ -358,8 +381,7 @@ public final class AaptExecLoopTask extends BaseTask {
task.setExecutable(mExecutable);
task.setFailonerror(true);
- File exe = new File(mExecutable);
- task.setTaskName(exe.getName());
+ task.setTaskName(getExecTaskName());
// aapt command. Only "package" is supported at this time really.
task.createArg().setValue(mCommand);
diff --git a/anttasks/src/com/android/ant/AidlExecTask.java b/anttasks/src/com/android/ant/AidlExecTask.java
index f2fa094..5fa1f30 100644
--- a/anttasks/src/com/android/ant/AidlExecTask.java
+++ b/anttasks/src/com/android/ant/AidlExecTask.java
@@ -93,9 +93,6 @@ public class AidlExecTask extends Task {
}
}
- File exe = new File(mExecutable);
- String execTaskName = exe.getName();
-
// now loop on all the source folders to find all the aidl to compile
// and compile them
for (String sourceFolder : sourceFolders) {
@@ -115,7 +112,7 @@ public class AidlExecTask extends Task {
task.setProject(taskProject);
task.setOwningTarget(getOwningTarget());
task.setExecutable(mExecutable);
- task.setTaskName(execTaskName);
+ task.setTaskName("aidl");
task.setFailonerror(true);
task.createArg().setValue("-p" + mFramework);
diff --git a/anttasks/src/com/android/ant/AntConstants.java b/anttasks/src/com/android/ant/AntConstants.java
deleted file mode 100644
index a87d0d5..0000000
--- a/anttasks/src/com/android/ant/AntConstants.java
+++ /dev/null
@@ -1,63 +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.ant;
-
-/**
- * Constants used by custom tasks and the rules files.
- */
-public interface AntConstants {
-
- /** ant property with the path to the android.jar file */
- public final static String PROP_ANDROID_JAR = "android.jar";
-
- /** ant property with the path to the framework.aidl file */
- public final static String PROP_ANDROID_AIDL = "android.aidl";
-
- /** ant property with the path to the renderscript framework include folder. */
- public final static String PROP_ANDROID_RENDERSCRIPT = "android.rs";
-
- /** ant property with the path to the aapt tool */
- public final static String PROP_AAPT = "aapt";
- /** ant property with the path to the aidl tool */
- public final static String PROP_AIDL = "aidl";
- /** ant property with the path to the dx tool */
- public final static String PROP_DX = "dx";
- /** ant property with the path to the renderscript tool */
- public final static String PROP_RENDERSCRIPT = "renderscript";
- /** ref id to the <path> object containing all the boot classpaths. */
- public final static String PROP_CLASSPATH_REF = "android.target.classpath";
-
- /** ant property ref to the list of source folder for the project libraries */
- public static final String PROP_PROJECT_LIBS_SRC_REF = "project.libraries.src";
- /** ant property ref to the list of jars for the project libraries */
- public static final String PROP_PROJECT_LIBS_JARS_REF = "project.libraries.jars";
- /** ant property ref to the list of libs folder for the project libraries */
- public static final String PROP_PROJECT_LIBS_LIBS_REF = "project.libraries.libs";
- /** ant property ref to the list of res folder for the project libraries */
- public static final String PROP_PROJECT_LIBS_RES_REF = "project.libraries.res";
- /** ant property for semi-colon separated packages for the project libraries */
- public static final String PROP_PROJECT_LIBS_PKG = "project.libraries.package";
- /** ant property for the test project directory */
- public static final String PROP_TESTED_PROJECT_DIR = "tested.project.dir";
-
- public static final String PROP_MANIFEST_PACKAGE = "manifest.package";
-
- public static final String PROP_OUT_ABS_DIR = "out.absolute.dir";
-
- public static final String PROP_KEY_STORE_PASSWORD = "key.store.password";
- public static final String PROP_KEY_ALIAS_PASSWORD = "key.alias.password";
-}
diff --git a/anttasks/src/com/android/ant/BaseTask.java b/anttasks/src/com/android/ant/BaseTask.java
index 2126d3f..00b7fcb 100644
--- a/anttasks/src/com/android/ant/BaseTask.java
+++ b/anttasks/src/com/android/ant/BaseTask.java
@@ -25,18 +25,22 @@ import java.util.ArrayList;
/**
* A base class for the ant task that contains logic for handling dependency files
*/
-public class BaseTask extends Task {
+public abstract class BaseTask extends Task {
private DependencyGraph mDependencies;
+ private String mPreviousBuildType;
+ private String mBuildType;
+
+ public void setPreviousBuildType(String previousBuildType) {
+ mPreviousBuildType = previousBuildType;
+ }
+
+ public void setBuildType(String buildType) {
+ mBuildType = buildType;
+ }
+
+ protected abstract String getExecTaskName();
- /*
- * (non-Javadoc)
- *
- * Executes the loop. Based on the values inside default.properties, this will
- * create alternate temporary ap_ files.
- *
- * @see org.apache.tools.ant.Task#execute()
- */
@Override
public void execute() throws BuildException {
@@ -46,9 +50,14 @@ public class BaseTask extends Task {
* Set up the dependency graph by passing it the location of the ".d" file
* @param dependencyFile path to the dependency file to use
* @param watchPaths a list of folders to watch for new files
- * @return true if the dependency graph was sucessfully initialized
+ * @return true if the dependency graph was successfully initialized
*/
protected boolean initDependencies(String dependencyFile, ArrayList<File> watchPaths) {
+ if (mBuildType != null && mBuildType.equals(mPreviousBuildType) == false) {
+ // we don't care about deps, we need to execute the task no matter what.
+ return true;
+ }
+
File depFile = new File(dependencyFile);
if (depFile.exists()) {
mDependencies = new DependencyGraph(dependencyFile, watchPaths);
@@ -64,6 +73,16 @@ public class BaseTask extends Task {
* have changed since the last run
*/
protected boolean dependenciesHaveChanged() {
+ if (mBuildType != null && mBuildType.equals(mPreviousBuildType) == false) {
+ String execName = getExecTaskName();
+ if (execName == null) {
+ System.out.println("Current build type is different than previous build: forced task run.");
+ } else {
+ System.out.println("Current build type is different than previous build: forced " + execName + " run.");
+ }
+ return true;
+ }
+
assert mDependencies != null : "Dependencies have not been initialized";
return mDependencies.dependenciesHaveChanged();
}
diff --git a/anttasks/src/com/android/ant/MultiApkExportTask.java b/anttasks/src/com/android/ant/MultiApkExportTask.java
index a21478e..6b23162 100644
--- a/anttasks/src/com/android/ant/MultiApkExportTask.java
+++ b/anttasks/src/com/android/ant/MultiApkExportTask.java
@@ -52,6 +52,12 @@ import javax.xml.xpath.XPathFactory;
*/
public class MultiApkExportTask extends Task {
+ private static final String PROP_OUT_ABS_DIR = "out.absolute.dir";
+
+ private static final String PROP_KEY_STORE_PASSWORD = "key.store.password";
+ private static final String PROP_KEY_ALIAS_PASSWORD = "key.alias.password";
+
+
private Target mTarget;
private XPathFactory mXPathFactory;
@@ -117,7 +123,7 @@ public class MultiApkExportTask extends Task {
mXPathFactory = XPathFactory.newInstance();
File exportProjectOutput = new File(
- getValidatedProperty(antProject, AntConstants.PROP_OUT_ABS_DIR));
+ getValidatedProperty(antProject, PROP_OUT_ABS_DIR));
// if there's no error, and we can sign, prompt for the passwords.
String keyStorePassword = null;
@@ -127,23 +133,21 @@ public class MultiApkExportTask extends Task {
Input input = new Input();
input.setProject(antProject);
- input.setAddproperty(AntConstants.PROP_KEY_STORE_PASSWORD);
+ input.setAddproperty(PROP_KEY_STORE_PASSWORD);
input.setMessage(String.format("Please enter keystore password (store: %1$s):",
keyStore));
input.execute();
input = new Input();
input.setProject(antProject);
- input.setAddproperty(AntConstants.PROP_KEY_ALIAS_PASSWORD);
+ input.setAddproperty(PROP_KEY_ALIAS_PASSWORD);
input.setMessage(String.format("Please enter password for alias '%1$s':",
keyAlias));
input.execute();
// and now read the property so that they can be set into the sub ant task.
- keyStorePassword = getValidatedProperty(antProject,
- AntConstants.PROP_KEY_STORE_PASSWORD);
- keyAliasPassword = getValidatedProperty(antProject,
- AntConstants.PROP_KEY_ALIAS_PASSWORD);
+ keyStorePassword = getValidatedProperty(antProject, PROP_KEY_STORE_PASSWORD);
+ keyAliasPassword = getValidatedProperty(antProject, PROP_KEY_ALIAS_PASSWORD);
}
for (ApkData apk : apks) {
diff --git a/anttasks/src/com/android/ant/NewSetupTask.java b/anttasks/src/com/android/ant/NewSetupTask.java
new file mode 100644
index 0000000..50dcdab
--- /dev/null
+++ b/anttasks/src/com/android/ant/NewSetupTask.java
@@ -0,0 +1,638 @@
+/*
+ * Copyright (C) 2009 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.ant;
+
+import com.android.io.FileWrapper;
+import com.android.io.FolderWrapper;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.IAndroidTarget;
+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.project.ProjectProperties;
+import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
+import com.android.sdklib.xml.AndroidManifest;
+import com.android.sdklib.xml.AndroidXPathFactory;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Path.PathElement;
+import org.apache.tools.ant.util.DeweyDecimal;
+import org.xml.sax.InputSource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathExpressionException;
+
+/**
+ * Setup Ant task. This task accomplishes:
+ * <ul>
+ * <li>Gets the project target hash string from {@link ProjectProperties#PROPERTY_TARGET},
+ * and resolves it to get the project's {@link IAndroidTarget}.</li>
+ *
+ * <li>Sets up properties so that aapt can find the android.jar and other files/folders in
+ * the resolved target.</li>
+ *
+ * <li>Sets up the boot classpath ref so that the <code>javac</code> task knows where to find
+ * the libraries. This includes the default android.jar from the resolved target but also optional
+ * libraries provided by the target (if any, when the target is an add-on).</li>
+ *
+ * <li>Resolve library dependencies and setup various Path references for them</li>
+ * </ul>
+ *
+ * This is used in the main rules file only.
+ *
+ */
+public class NewSetupTask extends Task {
+ private final static String ANT_MIN_VERSION = "1.8.0";
+
+ private String mProjectTypeOut;
+ private String mAndroidJarFileOut;
+ private String mAndroidAidlFileOut;
+ private String mRenderScriptExeOut;
+ private String mRenderScriptIncludeDirOut;
+ private String mBootclasspathrefOut;
+ private String mProjectLibrariesRootOut;
+ private String mProjectLibrariesResOut;
+ private String mProjectLibrariesPackageOut;
+ private String mProjectLibrariesJarsOut;
+ private String mProjectLibrariesLibsOut;
+
+ public void setProjectTypeOut(String projectTypeOut) {
+ mProjectTypeOut = projectTypeOut;
+ }
+
+ public void setAndroidJarFileOut(String androidJarFileOut) {
+ mAndroidJarFileOut = androidJarFileOut;
+ }
+
+ public void setAndroidAidlFileOut(String androidAidlFileOut) {
+ mAndroidAidlFileOut = androidAidlFileOut;
+ }
+
+ public void setRenderScriptExeOut(String renderScriptExeOut) {
+ mRenderScriptExeOut = renderScriptExeOut;
+ }
+
+ public void setRenderScriptIncludeDirOut(String renderScriptIncludeDirOut) {
+ mRenderScriptIncludeDirOut = renderScriptIncludeDirOut;
+ }
+
+ public void setBootclasspathrefOut(String bootclasspathrefOut) {
+ mBootclasspathrefOut = bootclasspathrefOut;
+ }
+
+ public void setProjectLibrariesRootOut(String projectLibrariesRootOut) {
+ mProjectLibrariesRootOut = projectLibrariesRootOut;
+ }
+
+ public void setProjectLibrariesResOut(String projectLibrariesResOut) {
+ mProjectLibrariesResOut = projectLibrariesResOut;
+ }
+
+ public void setProjectLibrariesPackageOut(String projectLibrariesPackageOut) {
+ mProjectLibrariesPackageOut = projectLibrariesPackageOut;
+ }
+
+ public void setProjectLibrariesJarsOut(String projectLibrariesJarsOut) {
+ mProjectLibrariesJarsOut = projectLibrariesJarsOut;
+ }
+
+ public void setProjectLibrariesLibsOut(String projectLibrariesLibsOut) {
+ mProjectLibrariesLibsOut = projectLibrariesLibsOut;
+ }
+
+ @Override
+ public void execute() throws BuildException {
+ if (mProjectTypeOut == null) {
+ throw new BuildException("Missing attribute projectTypeOut");
+ }
+ if (mAndroidJarFileOut == null) {
+ throw new BuildException("Missing attribute androidJarFileOut");
+ }
+ if (mAndroidAidlFileOut == null) {
+ throw new BuildException("Missing attribute androidAidlFileOut");
+ }
+ if (mRenderScriptExeOut == null) {
+ throw new BuildException("Missing attribute renderScriptExeOut");
+ }
+ if (mRenderScriptIncludeDirOut == null) {
+ throw new BuildException("Missing attribute renderScriptIncludeDirOut");
+ }
+ if (mBootclasspathrefOut == null) {
+ throw new BuildException("Missing attribute bootclasspathrefOut");
+ }
+ if (mProjectLibrariesRootOut == null) {
+ throw new BuildException("Missing attribute projectLibrariesRootOut");
+ }
+ if (mProjectLibrariesResOut == null) {
+ throw new BuildException("Missing attribute projectLibrariesResOut");
+ }
+ if (mProjectLibrariesPackageOut == null) {
+ throw new BuildException("Missing attribute projectLibrariesPackageOut");
+ }
+ if (mProjectLibrariesJarsOut == null) {
+ throw new BuildException("Missing attribute projectLibrariesJarsOut");
+ }
+ if (mProjectLibrariesLibsOut == null) {
+ throw new BuildException("Missing attribute projectLibrariesLibsOut");
+ }
+
+
+ Project antProject = getProject();
+
+ // check the Ant version
+ DeweyDecimal version = getVersion(antProject);
+ DeweyDecimal atLeast = new DeweyDecimal(ANT_MIN_VERSION);
+ if (atLeast.isGreaterThan(version)) {
+ throw new BuildException(
+ "The Android Ant-based build system requires Ant " +
+ ANT_MIN_VERSION +
+ " or later. Current version is " +
+ version);
+ }
+
+ // get the SDK location
+ File sdkDir = TaskHelper.getSdkLocation(antProject);
+ String sdkOsPath = sdkDir.getPath();
+
+ // Make sure the OS sdk path ends with a directory separator
+ if (sdkOsPath.length() > 0 && !sdkOsPath.endsWith(File.separator)) {
+ sdkOsPath += File.separator;
+ }
+
+ // display SDK Tools revision
+ int toolsRevison = TaskHelper.getToolsRevision(sdkDir);
+ if (toolsRevison != -1) {
+ System.out.println("Android SDK Tools Revision " + toolsRevison);
+ }
+
+ // detect that the platform tools is there.
+ File platformTools = new File(sdkDir, SdkConstants.FD_PLATFORM_TOOLS);
+ if (platformTools.isDirectory() == false) {
+ throw new BuildException(String.format(
+ "SDK Platform Tools component is missing. " +
+ "Please install it with the SDK Manager (%1$s%2$c%3$s)",
+ SdkConstants.FD_TOOLS,
+ File.separatorChar,
+ SdkConstants.androidCmdName()));
+ }
+
+ // get the target property value
+ String targetHashString = antProject.getProperty(ProjectProperties.PROPERTY_TARGET);
+
+ boolean isTestProject = false;
+
+ if (antProject.getProperty(ProjectProperties.PROPERTY_TESTED_PROJECT) != null) {
+ isTestProject = true;
+ }
+
+ if (targetHashString == null) {
+ throw new BuildException("Android Target is not set.");
+ }
+
+ // load up the sdk targets.
+ final ArrayList<String> messages = new ArrayList<String>();
+ SdkManager manager = SdkManager.createManager(sdkOsPath, new ISdkLog() {
+ public void error(Throwable t, String errorFormat, Object... args) {
+ if (errorFormat != null) {
+ messages.add(String.format("Error: " + errorFormat, args));
+ }
+ if (t != null) {
+ messages.add("Error: " + t.getMessage());
+ }
+ }
+
+ public void printf(String msgFormat, Object... args) {
+ messages.add(String.format(msgFormat, args));
+ }
+
+ public void warning(String warningFormat, Object... args) {
+ messages.add(String.format("Warning: " + warningFormat, args));
+ }
+ });
+
+ if (manager == null) {
+ // since we failed to parse the SDK, lets display the parsing output.
+ for (String msg : messages) {
+ System.out.println(msg);
+ }
+ throw new BuildException("Failed to parse SDK content.");
+ }
+
+ // resolve it
+ IAndroidTarget androidTarget = manager.getTargetFromHashString(targetHashString);
+
+ if (androidTarget == null) {
+ throw new BuildException(String.format(
+ "Unable to resolve target '%s'", targetHashString));
+ }
+
+ // display the project info
+ System.out.println("Project Target: " + androidTarget.getName());
+ if (androidTarget.isPlatform() == false) {
+ System.out.println("Vendor: " + androidTarget.getVendor());
+ System.out.println("Platform Version: " + androidTarget.getVersionName());
+ }
+ System.out.println("API level: " + androidTarget.getVersion().getApiString());
+
+ // check if the project is a library
+ boolean isLibrary = false;
+
+ String libraryProp = antProject.getProperty(ProjectProperties.PROPERTY_LIBRARY);
+ if (libraryProp != null) {
+ isLibrary = Boolean.valueOf(libraryProp).booleanValue();
+ }
+
+ if (isLibrary) {
+ System.out.println("Project Type: Android Library");
+ }
+
+ // look for referenced libraries.
+ processReferencedLibraries(antProject, androidTarget);
+
+ // always check the manifest minSdkVersion.
+ checkManifest(antProject, androidTarget.getVersion());
+
+ // sets up the properties to find android.jar/framework.aidl/target tools
+ String androidJar = androidTarget.getPath(IAndroidTarget.ANDROID_JAR);
+ antProject.setProperty(mAndroidJarFileOut, androidJar);
+
+ String androidAidl = androidTarget.getPath(IAndroidTarget.ANDROID_AIDL);
+ antProject.setProperty(mAndroidAidlFileOut, androidAidl);
+
+ Path includePath = new Path(antProject);
+ PathElement element = includePath.createPathElement();
+ element.setPath(androidTarget.getPath(IAndroidTarget.ANDROID_RS));
+ element = includePath.createPathElement();
+ element.setPath(androidTarget.getPath(IAndroidTarget.ANDROID_RS_CLANG));
+ antProject.setProperty(mRenderScriptIncludeDirOut, includePath.toString());
+
+ // TODO: figure out the actual compiler to use based on the minSdkVersion
+ antProject.setProperty(mRenderScriptExeOut,
+ sdkOsPath + SdkConstants.OS_SDK_PLATFORM_TOOLS_FOLDER +
+ SdkConstants.FN_RENDERSCRIPT);
+
+ // sets up the boot classpath
+
+ // create the Path object
+ Path bootclasspath = new Path(antProject);
+
+ // create a PathElement for the framework jar
+ element = bootclasspath.createPathElement();
+ element.setPath(androidJar);
+
+ // create PathElement for each optional library.
+ IOptionalLibrary[] libraries = androidTarget.getOptionalLibraries();
+ if (libraries != null) {
+ HashSet<String> visitedJars = new HashSet<String>();
+ for (IOptionalLibrary library : libraries) {
+ String jarPath = library.getJarPath();
+ if (visitedJars.contains(jarPath) == false) {
+ visitedJars.add(jarPath);
+
+ element = bootclasspath.createPathElement();
+ element.setPath(library.getJarPath());
+ }
+ }
+ }
+
+ // sets the path in the project with a reference
+ antProject.addReference(mBootclasspathrefOut, bootclasspath);
+
+ // finally set the project type.
+ if (isLibrary) {
+ antProject.setProperty(mProjectTypeOut, "library");
+ } else if (isTestProject) {
+ antProject.setProperty(mProjectTypeOut, "test");
+ } else {
+ antProject.setProperty(mProjectTypeOut, "project");
+ }
+ }
+
+ /**
+ * Checks the manifest <code>minSdkVersion</code> attribute.
+ * @param antProject the ant project
+ * @param androidVersion the version of the platform the project is compiling against.
+ */
+ private void checkManifest(Project antProject, AndroidVersion androidVersion) {
+ try {
+ File manifest = new File(antProject.getBaseDir(), SdkConstants.FN_ANDROID_MANIFEST_XML);
+
+ XPath xPath = AndroidXPathFactory.newXPath();
+
+ // check the package name.
+ String value = xPath.evaluate(
+ "/" + AndroidManifest.NODE_MANIFEST +
+ "/@" + AndroidManifest.ATTRIBUTE_PACKAGE,
+ new InputSource(new FileInputStream(manifest)));
+ if (value != null) { // aapt will complain if it's missing.
+ // only need to check that the package has 2 segments
+ if (value.indexOf('.') == -1) {
+ throw new BuildException(String.format(
+ "Application package '%1$s' must have a minimum of 2 segments.",
+ value));
+ }
+ }
+
+ // check the minSdkVersion value
+ value = xPath.evaluate(
+ "/" + AndroidManifest.NODE_MANIFEST +
+ "/" + AndroidManifest.NODE_USES_SDK +
+ "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + ":" +
+ AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION,
+ new InputSource(new FileInputStream(manifest)));
+
+ if (androidVersion.isPreview()) {
+ // in preview mode, the content of the minSdkVersion must match exactly the
+ // platform codename.
+ String codeName = androidVersion.getCodename();
+ if (codeName.equals(value) == false) {
+ throw new BuildException(String.format(
+ "For '%1$s' SDK Preview, attribute minSdkVersion in AndroidManifest.xml must be '%1$s'",
+ codeName));
+ }
+ } else if (value.length() > 0) {
+ // for normal platform, we'll only display warnings if the value is lower or higher
+ // than the target api level.
+ // First convert to an int.
+ int minSdkValue = -1;
+ try {
+ minSdkValue = Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ // looks like it's not a number: error!
+ throw new BuildException(String.format(
+ "Attribute %1$s in AndroidManifest.xml must be an Integer!",
+ AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION));
+ }
+
+ int projectApiLevel = androidVersion.getApiLevel();
+ if (minSdkValue < projectApiLevel) {
+ System.out.println(String.format(
+ "WARNING: Attribute %1$s in AndroidManifest.xml (%2$d) is lower than the project target API level (%3$d)",
+ AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION,
+ minSdkValue, projectApiLevel));
+ } else if (minSdkValue > androidVersion.getApiLevel()) {
+ System.out.println(String.format(
+ "WARNING: Attribute %1$s in AndroidManifest.xml (%2$d) is higher than the project target API level (%3$d)",
+ AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION,
+ minSdkValue, projectApiLevel));
+ }
+ } else {
+ // no minSdkVersion? display a warning
+ System.out.println(
+ "WARNING: No minSdkVersion value set. Application will install on all Android versions.");
+ }
+
+ } catch (XPathExpressionException e) {
+ throw new BuildException(e);
+ } catch (FileNotFoundException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ private void processReferencedLibraries(Project antProject, IAndroidTarget androidTarget) {
+ // prepare several paths for future tasks
+ Path rootPath = new Path(antProject);
+ Path resPath = new Path(antProject);
+ Path libsPath = new Path(antProject);
+ Path jarsPath = new Path(antProject);
+ StringBuilder packageStrBuilder = new StringBuilder();
+
+ FilenameFilter filter = new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return name.toLowerCase().endsWith(".jar");
+ }
+ };
+
+ System.out.println("\n------------------\nResolving library dependencies:");
+
+ ArrayList<File> libraries = getProjectLibraries(antProject);
+
+ if (libraries.size() > 0) {
+ System.out.println("------------------\nOrdered libraries:");
+
+ for (File library : libraries) {
+ String libRootPath = library.getAbsolutePath();
+ System.out.println(libRootPath);
+
+ // get the root path.
+ PathElement element = rootPath.createPathElement();
+ element.setPath(libRootPath);
+
+ // get the res path. Always $PROJECT/res
+ element = resPath.createPathElement();
+ element.setPath(libRootPath + "/" + SdkConstants.FD_RESOURCES);
+
+ // get the libs path. Always $PROJECT/libs
+ element = libsPath.createPathElement();
+ element.setPath(libRootPath + "/" + SdkConstants.FD_NATIVE_LIBS);
+
+ // get the jars from it too.
+ // 1. the library code jar
+ element = jarsPath.createPathElement();
+ element.setPath(libRootPath + "/" + SdkConstants.FD_OUTPUT +
+ "/" + SdkConstants.FN_CLASSES_JAR);
+
+ // 2. the 3rd party jar files
+ File libsFolder = new File(library, SdkConstants.FD_NATIVE_LIBS);
+ File[] jarFiles = libsFolder.listFiles(filter);
+ if (jarFiles != null) {
+ for (File jarFile : jarFiles) {
+ element = jarsPath.createPathElement();
+ element.setPath(jarFile.getAbsolutePath());
+ }
+ }
+
+ // get the package from the manifest.
+ FileWrapper manifest = new FileWrapper(library,
+ SdkConstants.FN_ANDROID_MANIFEST_XML);
+
+ try {
+ String value = AndroidManifest.getPackage(manifest);
+ if (value != null) { // aapt will complain if it's missing.
+ packageStrBuilder.append(';');
+ packageStrBuilder.append(value);
+ }
+ } catch (Exception e) {
+ throw new BuildException(e);
+ }
+ }
+ } else {
+ System.out.println("No library dependencies.\n");
+ }
+
+ System.out.println("------------------\n");
+
+ // even with no libraries, always setup these so that various tasks in Ant don't complain
+ // (the task themselves can handle a ref to an empty Path)
+ antProject.addReference(mProjectLibrariesJarsOut, jarsPath);
+ antProject.addReference(mProjectLibrariesLibsOut, libsPath);
+
+ // the rest is done only if there's a library.
+ if (jarsPath.list().length > 0) {
+ System.out.println("DEBUG: " + rootPath.toString());
+ antProject.addReference(mProjectLibrariesRootOut, rootPath);
+ antProject.addReference(mProjectLibrariesResOut, resPath);
+ antProject.setProperty(mProjectLibrariesPackageOut, packageStrBuilder.toString());
+ }
+ }
+
+ /**
+ * Returns all the library dependencies of a given Ant project.
+ * @param antProject the Ant project
+ * @return a list of properties, sorted from highest priority to lowest.
+ */
+ private ArrayList<File> getProjectLibraries(final Project antProject) {
+ ArrayList<File> libraries = new ArrayList<File>();
+ File baseDir = antProject.getBaseDir();
+
+ // get the top level list of library dependencies.
+ List<File> topLevelLibraries = getDirectDependencies(baseDir, new IPropertySource() {
+ public String getProperty(String name) {
+ return antProject.getProperty(name);
+ }
+ });
+
+ // process the libraries in case they depend on other libraries.
+ resolveFullLibraryDependencies(topLevelLibraries, libraries);
+
+ return libraries;
+ }
+
+ /**
+ * Resolves a given list of libraries, finds out if they depend on other libraries, and
+ * returns a full list of all the direct and indirect dependencies in the proper order (first
+ * is higher priority when calling aapt).
+ * @param inLibraries the libraries to resolve
+ * @param outLibraries where to store all the libraries.
+ */
+ private void resolveFullLibraryDependencies(List<File> inLibraries, List<File> outLibraries) {
+ // loop in the inverse order to resolve dependencies on the libraries, so that if a library
+ // is required by two higher level libraries it can be inserted in the correct place
+ for (int i = inLibraries.size() - 1 ; i >= 0 ; i--) {
+ File library = inLibraries.get(i);
+
+ // get the default.property file for it
+ final ProjectProperties defaultProp = ProjectProperties.load(
+ new FolderWrapper(library), PropertyType.DEFAULT);
+
+ // get its libraries
+ List<File> dependencies = getDirectDependencies(library, new IPropertySource() {
+ public String getProperty(String name) {
+ return defaultProp.getProperty(name);
+ }
+ });
+
+ // resolve the dependencies for those libraries
+ resolveFullLibraryDependencies(dependencies, outLibraries);
+
+ // and add the current one (if needed) in front (higher priority)
+ if (outLibraries.contains(library) == false) {
+ outLibraries.add(0, library);
+ }
+ }
+ }
+
+ public interface IPropertySource {
+ String getProperty(String name);
+ }
+
+ /**
+ * Returns the top level library dependencies of a given <var>source</var> representing a
+ * project properties.
+ * @param baseFolder the base folder of the project (to resolve relative paths)
+ * @param source a source of project properties.
+ */
+ private List<File> getDirectDependencies(File baseFolder, IPropertySource source) {
+ ArrayList<File> libraries = new ArrayList<File>();
+
+ // first build the list. they are ordered highest priority first.
+ int index = 1;
+ while (true) {
+ String propName = ProjectProperties.PROPERTY_LIB_REF + Integer.toString(index++);
+ String rootPath = source.getProperty(propName);
+
+ if (rootPath == null) {
+ break;
+ }
+
+ try {
+ File library = new File(baseFolder, rootPath).getCanonicalFile();
+
+ // check for validity
+ File defaultProp = new File(library, PropertyType.DEFAULT.getFilename());
+ if (defaultProp.isFile() == false) {
+ // error!
+ throw new BuildException(String.format(
+ "%1$s resolve to a path with no %2$s file for project %3$s", rootPath,
+ PropertyType.DEFAULT.getFilename(), baseFolder.getAbsolutePath()));
+ }
+
+ if (libraries.contains(library) == false) {
+ System.out.println(String.format("%1$s: %2$s => %3$s",
+ baseFolder.getAbsolutePath(), rootPath, library.getAbsolutePath()));
+
+ libraries.add(library);
+ }
+ } catch (IOException e) {
+ throw new BuildException("Failed to resolve library path: " + rootPath, e);
+ }
+ }
+
+ return libraries;
+ }
+
+ /**
+ * Returns the Ant version as a {@link DeweyDecimal} object.
+ *
+ * This is based on the implementation of
+ * org.apache.tools.ant.taskdefs.condition.AntVersion.getVersion()
+ *
+ * @param antProject the current ant project.
+ * @return the ant version.
+ */
+ private DeweyDecimal getVersion(Project antProject) {
+ char[] versionString = antProject.getProperty("ant.version").toCharArray();
+ StringBuilder sb = new StringBuilder();
+ boolean foundFirstDigit = false;
+ for (int i = 0; i < versionString.length; i++) {
+ if (Character.isDigit(versionString[i])) {
+ sb.append(versionString[i]);
+ foundFirstDigit = true;
+ }
+ if (versionString[i] == '.' && foundFirstDigit) {
+ sb.append(versionString[i]);
+ }
+ if (Character.isLetter(versionString[i]) && foundFirstDigit) {
+ break;
+ }
+ }
+ return new DeweyDecimal(sb.toString());
+ }
+}
diff --git a/anttasks/src/com/android/ant/SetupTask.java b/anttasks/src/com/android/ant/SetupTask.java
index e15f77b..c0bc55d 100644
--- a/anttasks/src/com/android/ant/SetupTask.java
+++ b/anttasks/src/com/android/ant/SetupTask.java
@@ -16,588 +16,31 @@
package com.android.ant;
-import com.android.io.FileWrapper;
-import com.android.io.FolderWrapper;
-import com.android.sdklib.AndroidVersion;
-import com.android.sdklib.IAndroidTarget;
-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.project.ProjectProperties;
-import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
-import com.android.sdklib.xml.AndroidManifest;
-import com.android.sdklib.xml.AndroidXPathFactory;
-
import org.apache.tools.ant.BuildException;
-import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.ImportTask;
-import org.apache.tools.ant.types.Path;
-import org.apache.tools.ant.types.Path.PathElement;
-import org.apache.tools.ant.util.DeweyDecimal;
-import org.xml.sax.InputSource;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashSet;
-
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathExpressionException;
/**
- * Setup/Import Ant task. This task accomplishes:
- * <ul>
- * <li>Gets the project target hash string from {@link ProjectProperties#PROPERTY_TARGET},
- * and resolves it to get the project's {@link IAndroidTarget}.</li>
- * <li>Sets up properties so that aapt can find the android.jar in the resolved target.</li>
- * <li>Sets up the boot classpath ref so that the <code>javac</code> task knows where to find
- * the libraries. This includes the default android.jar from the resolved target but also optional
- * libraries provided by the target (if any, when the target is an add-on).</li>
- * <li>Imports the build rules located in the resolved target so that the build actually does
- * something. This can be disabled with the attribute <var>import</var> set to <code>false</code>
- * </li></ul>
- *
- * This is used in build.xml/template.
+ * Legacy setupTask class used by older build system.
*
+ * If this is used it actually only display an error about the need to update the build file.
*/
-public final class SetupTask extends ImportTask {
- private final static String ANT_MIN_VERSION = "1.8.0";
- // main rules file
- private final static String RULES_MAIN = "main_rules.xml";
- // test rules file - depends on android_rules.xml
- private final static String RULES_TEST = "test_rules.xml";
- // library rules file.
- private final static String RULES_LIBRARY = "lib_rules.xml";
-
- private boolean mDoImport = true;
-
- @Override
- public void execute() throws BuildException {
- Project antProject = getProject();
-
- // check the Ant version
- DeweyDecimal version = getVersion(antProject);
- DeweyDecimal atLeast = new DeweyDecimal(ANT_MIN_VERSION);
- if (atLeast.isGreaterThan(version)) {
- throw new BuildException(
- "The Android Ant-based build system requires Ant " +
- ANT_MIN_VERSION +
- " or later. Current version is " +
- version);
- }
-
- // get the SDK location
- File sdkDir = TaskHelper.getSdkLocation(antProject);
- String sdkOsPath = sdkDir.getPath();
-
- // Make sure the OS sdk path ends with a directory separator
- if (sdkOsPath.length() > 0 && !sdkOsPath.endsWith(File.separator)) {
- sdkOsPath += File.separator;
- }
-
- // display SDK Tools revision
- int toolsRevison = TaskHelper.getToolsRevision(sdkDir);
- if (toolsRevison != -1) {
- System.out.println("Android SDK Tools Revision " + toolsRevison);
- }
-
- // detect that the platform tools is there.
- File platformTools = new File(sdkDir, SdkConstants.FD_PLATFORM_TOOLS);
- if (platformTools.isDirectory() == false) {
- throw new BuildException(String.format(
- "SDK Platform Tools component is missing. " +
- "Please install it with the SDK Manager (%1$s%2$c%3$s)",
- SdkConstants.FD_TOOLS,
- File.separatorChar,
- SdkConstants.androidCmdName()));
- }
-
- // get the target property value
- String targetHashString = antProject.getProperty(ProjectProperties.PROPERTY_TARGET);
-
- boolean isTestProject = false;
-
- if (antProject.getProperty(AntConstants.PROP_TESTED_PROJECT_DIR) != null) {
- isTestProject = true;
- }
-
- if (targetHashString == null) {
- throw new BuildException("Android Target is not set.");
- }
-
- // load up the sdk targets.
- final ArrayList<String> messages = new ArrayList<String>();
- SdkManager manager = SdkManager.createManager(sdkOsPath, new ISdkLog() {
- public void error(Throwable t, String errorFormat, Object... args) {
- if (errorFormat != null) {
- messages.add(String.format("Error: " + errorFormat, args));
- }
- if (t != null) {
- messages.add("Error: " + t.getMessage());
- }
- }
-
- public void printf(String msgFormat, Object... args) {
- messages.add(String.format(msgFormat, args));
- }
-
- public void warning(String warningFormat, Object... args) {
- messages.add(String.format("Warning: " + warningFormat, args));
- }
- });
-
- if (manager == null) {
- // since we failed to parse the SDK, lets display the parsing output.
- for (String msg : messages) {
- System.out.println(msg);
- }
- throw new BuildException("Failed to parse SDK content.");
- }
-
- // resolve it
- IAndroidTarget androidTarget = manager.getTargetFromHashString(targetHashString);
-
- if (androidTarget == null) {
- throw new BuildException(String.format(
- "Unable to resolve target '%s'", targetHashString));
- }
-
- // display the project info
- System.out.println("Project Target: " + androidTarget.getName());
- if (androidTarget.isPlatform() == false) {
- System.out.println("Vendor: " + androidTarget.getVendor());
- System.out.println("Platform Version: " + androidTarget.getVersionName());
- }
- System.out.println("API level: " + androidTarget.getVersion().getApiString());
-
- // check if the project is a library
- boolean isLibrary = false;
-
- String libraryProp = antProject.getProperty(ProjectProperties.PROPERTY_LIBRARY);
- if (libraryProp != null) {
- isLibrary = Boolean.valueOf(libraryProp).booleanValue();
- }
-
- if (isLibrary) {
- System.out.println("Project Type: Android Library");
- }
-
- // look for referenced libraries.
- processReferencedLibraries(antProject, androidTarget);
-
- // always check the manifest minSdkVersion.
- checkManifest(antProject, androidTarget.getVersion());
-
- // sets up the properties to find android.jar/framework.aidl/target tools
- String androidJar = androidTarget.getPath(IAndroidTarget.ANDROID_JAR);
- antProject.setProperty(AntConstants.PROP_ANDROID_JAR, androidJar);
-
- String androidAidl = androidTarget.getPath(IAndroidTarget.ANDROID_AIDL);
- antProject.setProperty(AntConstants.PROP_ANDROID_AIDL, androidAidl);
-
- Path includePath = new Path(antProject);
- PathElement element = includePath.createPathElement();
- element.setPath(androidTarget.getPath(IAndroidTarget.ANDROID_RS));
- element = includePath.createPathElement();
- element.setPath(androidTarget.getPath(IAndroidTarget.ANDROID_RS_CLANG));
- antProject.setProperty(AntConstants.PROP_ANDROID_RENDERSCRIPT, includePath.toString());
-
- antProject.setProperty(AntConstants.PROP_AAPT, androidTarget.getPath(IAndroidTarget.AAPT));
- antProject.setProperty(AntConstants.PROP_AIDL, androidTarget.getPath(IAndroidTarget.AIDL));
- antProject.setProperty(AntConstants.PROP_DX, androidTarget.getPath(IAndroidTarget.DX));
- antProject.setProperty(AntConstants.PROP_RENDERSCRIPT,
- sdkOsPath + SdkConstants.OS_SDK_PLATFORM_TOOLS_FOLDER +
- SdkConstants.FN_RENDERSCRIPT);
-
- // sets up the boot classpath
-
- // create the Path object
- Path bootclasspath = new Path(antProject);
-
- // create a PathElement for the framework jar
- element = bootclasspath.createPathElement();
- element.setPath(androidJar);
-
- // create PathElement for each optional library.
- IOptionalLibrary[] libraries = androidTarget.getOptionalLibraries();
- if (libraries != null) {
- HashSet<String> visitedJars = new HashSet<String>();
- for (IOptionalLibrary library : libraries) {
- String jarPath = library.getJarPath();
- if (visitedJars.contains(jarPath) == false) {
- visitedJars.add(jarPath);
-
- element = bootclasspath.createPathElement();
- element.setPath(library.getJarPath());
- }
- }
- }
-
- // finally sets the path in the project with a reference
- antProject.addReference(AntConstants.PROP_CLASSPATH_REF, bootclasspath);
-
- // Now the import section. This is only executed if the task actually has to import a file.
- if (mDoImport) {
- // check the ant folder exists in the tools folder of the SDK.
- File rulesFolder = new File(
- new File(sdkOsPath, SdkConstants.FD_TOOLS),
- SdkConstants.FD_ANT);
-
- // make sure the file exists.
- if (rulesFolder.isDirectory() == false) {
- throw new BuildException(String.format("Rules directory '%s' is missing.",
- rulesFolder.getAbsolutePath()));
- }
-
- // name of the rules files to import based on the type of project
- String importedRulesFileName =
- isLibrary ? RULES_LIBRARY : isTestProject ? RULES_TEST : RULES_MAIN;
-
- // now check the rules file exists.
- File rules = new File(rulesFolder, importedRulesFileName);
-
- if (rules.isFile() == false) {
- throw new BuildException(String.format("Build rules file '%s' is missing.",
- rules));
- }
-
- // display the file being imported.
- // figure out the path relative to the SDK
- String rulesOsPath = rules.getAbsolutePath();
- if (rulesOsPath.startsWith(sdkOsPath)) {
- rulesOsPath = rulesOsPath.substring(sdkOsPath.length());
- if (rulesOsPath.startsWith(File.separator)) {
- rulesOsPath = rulesOsPath.substring(1);
- }
- }
- System.out.println("\nImporting rules file: " + rulesOsPath);
-
- // set the file location to import
- setFile(rules.getAbsolutePath());
-
- // and import
- super.execute();
- }
- }
-
- /**
- * Sets the value of the "import" attribute.
- * @param value the value.
- */
- public void setImport(boolean value) {
- mDoImport = value;
- }
+public final class SetupTask extends Task {
/**
- * Checks the manifest <code>minSdkVersion</code> attribute.
- * @param antProject the ant project
- * @param androidVersion the version of the platform the project is compiling against.
- */
- private void checkManifest(Project antProject, AndroidVersion androidVersion) {
- try {
- File manifest = new File(antProject.getBaseDir(), SdkConstants.FN_ANDROID_MANIFEST_XML);
-
- XPath xPath = AndroidXPathFactory.newXPath();
-
- // check the package name.
- String value = xPath.evaluate(
- "/" + AndroidManifest.NODE_MANIFEST +
- "/@" + AndroidManifest.ATTRIBUTE_PACKAGE,
- new InputSource(new FileInputStream(manifest)));
- if (value != null) { // aapt will complain if it's missing.
- // only need to check that the package has 2 segments
- if (value.indexOf('.') == -1) {
- throw new BuildException(String.format(
- "Application package '%1$s' must have a minimum of 2 segments.",
- value));
- }
- }
-
- // check the minSdkVersion value
- value = xPath.evaluate(
- "/" + AndroidManifest.NODE_MANIFEST +
- "/" + AndroidManifest.NODE_USES_SDK +
- "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + ":" +
- AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION,
- new InputSource(new FileInputStream(manifest)));
-
- if (androidVersion.isPreview()) {
- // in preview mode, the content of the minSdkVersion must match exactly the
- // platform codename.
- String codeName = androidVersion.getCodename();
- if (codeName.equals(value) == false) {
- throw new BuildException(String.format(
- "For '%1$s' SDK Preview, attribute minSdkVersion in AndroidManifest.xml must be '%1$s'",
- codeName));
- }
- } else if (value.length() > 0) {
- // for normal platform, we'll only display warnings if the value is lower or higher
- // than the target api level.
- // First convert to an int.
- int minSdkValue = -1;
- try {
- minSdkValue = Integer.parseInt(value);
- } catch (NumberFormatException e) {
- // looks like it's not a number: error!
- throw new BuildException(String.format(
- "Attribute %1$s in AndroidManifest.xml must be an Integer!",
- AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION));
- }
-
- int projectApiLevel = androidVersion.getApiLevel();
- if (minSdkValue < projectApiLevel) {
- System.out.println(String.format(
- "WARNING: Attribute %1$s in AndroidManifest.xml (%2$d) is lower than the project target API level (%3$d)",
- AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION,
- minSdkValue, projectApiLevel));
- } else if (minSdkValue > androidVersion.getApiLevel()) {
- System.out.println(String.format(
- "WARNING: Attribute %1$s in AndroidManifest.xml (%2$d) is higher than the project target API level (%3$d)",
- AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION,
- minSdkValue, projectApiLevel));
- }
- } else {
- // no minSdkVersion? display a warning
- System.out.println(
- "WARNING: No minSdkVersion value set. Application will install on all Android versions.");
- }
-
- } catch (XPathExpressionException e) {
- throw new BuildException(e);
- } catch (FileNotFoundException e) {
- throw new BuildException(e);
- }
- }
-
- private void processReferencedLibraries(Project antProject, IAndroidTarget androidTarget) {
- // prepare several paths for future tasks
- Path sourcePath = new Path(antProject);
- Path resPath = new Path(antProject);
- Path libsPath = new Path(antProject);
- Path jarsPath = new Path(antProject);
- StringBuilder sb = new StringBuilder();
-
- FilenameFilter filter = new FilenameFilter() {
- public boolean accept(File dir, String name) {
- return name.toLowerCase().endsWith(".jar");
- }
- };
-
- System.out.println("\n------------------\nResolving library dependencies:");
-
- ArrayList<File> libraries = getProjectLibraries(antProject);
-
- if (libraries.size() > 0) {
- System.out.println("------------------\nOrdered libraries:");
-
- for (File library : libraries) {
- System.out.println(library.getAbsolutePath());
-
- // get the source path. default is src but can be overriden by the property
- // "source.dir" in build.properties.
- PathElement element = sourcePath.createPathElement();
- ProjectProperties prop = ProjectProperties.load(new FolderWrapper(library),
- PropertyType.BUILD);
-
- String sourceDir = SdkConstants.FD_SOURCES;
- if (prop != null) {
- String value = prop.getProperty(ProjectProperties.PROPERTY_BUILD_SOURCE_DIR);
- if (value != null) {
- sourceDir = value;
- }
- }
-
- String path = library.getAbsolutePath();
-
- element.setPath(path + "/" + sourceDir);
-
- // get the res path. Always $PROJECT/res
- element = resPath.createPathElement();
- element.setPath(path + "/" + SdkConstants.FD_RESOURCES);
-
- // get the libs path. Always $PROJECT/libs
- element = libsPath.createPathElement();
- element.setPath(path + "/" + SdkConstants.FD_NATIVE_LIBS);
-
- // get the jars from it too
- File libsFolder = new File(library, SdkConstants.FD_NATIVE_LIBS);
- File[] jarFiles = libsFolder.listFiles(filter);
- if (jarFiles != null) {
- for (File jarFile : jarFiles) {
- element = jarsPath.createPathElement();
- element.setPath(jarFile.getAbsolutePath());
- }
- }
-
- // get the package from the manifest.
- FileWrapper manifest = new FileWrapper(library,
- SdkConstants.FN_ANDROID_MANIFEST_XML);
-
- try {
- String value = AndroidManifest.getPackage(manifest);
- if (value != null) { // aapt will complain if it's missing.
- sb.append(';');
- sb.append(value);
- }
- } catch (Exception e) {
- throw new BuildException(e);
- }
- }
- } else {
- System.out.println("No library dependencies.\n");
- }
-
- System.out.println("------------------\n");
-
- // even with no libraries, always setup these so that various tasks in Ant don't complain
- // (the task themselves can handle a ref to an empty Path)
- antProject.addReference(AntConstants.PROP_PROJECT_LIBS_SRC_REF, sourcePath);
- antProject.addReference(AntConstants.PROP_PROJECT_LIBS_JARS_REF, jarsPath);
- antProject.addReference(AntConstants.PROP_PROJECT_LIBS_LIBS_REF, libsPath);
-
- // the rest is done only if there's a library.
- if (sourcePath.list().length > 0) {
- antProject.addReference(AntConstants.PROP_PROJECT_LIBS_RES_REF, resPath);
- antProject.setProperty(AntConstants.PROP_PROJECT_LIBS_PKG, sb.toString());
- }
- }
-
- /**
- * Returns all the library dependencies of a given Ant project.
- * @param antProject the Ant project
- * @return a list of properties, sorted from highest priority to lowest.
- */
- private ArrayList<File> getProjectLibraries(final Project antProject) {
- ArrayList<File> libraries = new ArrayList<File>();
- File baseDir = antProject.getBaseDir();
-
- // get the top level list of library dependencies.
- ArrayList<File> topLevelLibraries = getDirectDependencies(baseDir, new IPropertySource() {
- public String getProperty(String name) {
- return antProject.getProperty(name);
- }
- });
-
- // process the libraries in case they depend on other libraries.
- resolveFullLibraryDependencies(topLevelLibraries, libraries);
-
- return libraries;
- }
-
- /**
- * Resolves a given list of libraries, finds out if they depend on other libraries, and
- * returns a full list of all the direct and indirect dependencies in the proper order (first
- * is higher priority when calling aapt).
- * @param inLibraries the libraries to resolve
- * @param outLibraries where to store all the libraries.
- */
- private void resolveFullLibraryDependencies(ArrayList<File> inLibraries,
- ArrayList<File> outLibraries) {
- // loop in the inverse order to resolve dependencies on the libraries, so that if a library
- // is required by two higher level libraries it can be inserted in the correct place
- for (int i = inLibraries.size() - 1 ; i >= 0 ; i--) {
- File library = inLibraries.get(i);
-
- // get the default.property file for it
- final ProjectProperties defaultProp = ProjectProperties.load(
- new FolderWrapper(library), PropertyType.DEFAULT);
-
- // get its libraries
- ArrayList<File> dependencies = getDirectDependencies(library, new IPropertySource() {
- public String getProperty(String name) {
- return defaultProp.getProperty(name);
- }
- });
-
- // resolve the dependencies for those libraries
- resolveFullLibraryDependencies(dependencies, outLibraries);
-
- // and add the current one (if needed) in front (higher priority)
- if (outLibraries.contains(library) == false) {
- outLibraries.add(0, library);
- }
- }
- }
-
- public interface IPropertySource {
- String getProperty(String name);
- }
-
- /**
- * Returns the top level library dependencies of a given <var>source</var> representing a
- * project properties.
- * @param baseFolder the base folder of the project (to resolve relative paths)
- * @param source a source of project properties.
- */
- private ArrayList<File> getDirectDependencies(File baseFolder, IPropertySource source) {
- ArrayList<File> libraries = new ArrayList<File>();
-
- // first build the list. they are ordered highest priority first.
- int index = 1;
- while (true) {
- String propName = ProjectProperties.PROPERTY_LIB_REF + Integer.toString(index++);
- String rootPath = source.getProperty(propName);
-
- if (rootPath == null) {
- break;
- }
-
- try {
- File library = new File(baseFolder, rootPath).getCanonicalFile();
-
- // check for validity
- File defaultProp = new File(library, PropertyType.DEFAULT.getFilename());
- if (defaultProp.isFile() == false) {
- // error!
- throw new BuildException(String.format(
- "%1$s resolve to a path with no %2$s file for project %3$s", rootPath,
- PropertyType.DEFAULT.getFilename(), baseFolder.getAbsolutePath()));
- }
-
- if (libraries.contains(library) == false) {
- System.out.println(String.format("%1$s: %2$s => %3$s",
- baseFolder.getAbsolutePath(), rootPath, library.getAbsolutePath()));
-
- libraries.add(library);
- }
- } catch (IOException e) {
- throw new BuildException("Failed to resolve library path: " + rootPath, e);
- }
- }
-
- return libraries;
- }
-
- /**
- * Returns the Ant version as a {@link DeweyDecimal} object.
- *
- * This is based on the implementation of
- * org.apache.tools.ant.taskdefs.condition.AntVersion.getVersion()
+ * @param b unused.
*
- * @param antProject the current ant project.
- * @return the ant version.
+ * @deprecated only present because the original {@link SetupTask} extends {@link ImportTask}.
*/
- private DeweyDecimal getVersion(Project antProject) {
- char[] versionString = antProject.getProperty("ant.version").toCharArray();
- StringBuffer sb = new StringBuffer();
- boolean foundFirstDigit = false;
- for (int i = 0; i < versionString.length; i++) {
- if (Character.isDigit(versionString[i])) {
- sb.append(versionString[i]);
- foundFirstDigit = true;
- }
- if (versionString[i] == '.' && foundFirstDigit) {
- sb.append(versionString[i]);
- }
- if (Character.isLetter(versionString[i]) && foundFirstDigit) {
- break;
- }
- }
- return new DeweyDecimal(sb.toString());
+ @Deprecated
+ public void setImport(boolean b) {
+ // do nothing
}
+ @Override
+ public void execute() throws BuildException {
+ throw new BuildException("\n\nError. You are using an obsolete build.xml\n" +
+ "You need to delete it and regenerate it using\n" +
+ "\tandroid update project\n");
+ }
}
diff --git a/changes.txt b/changes.txt
index 73bd929..fcb0f3c 100644
--- a/changes.txt
+++ b/changes.txt
@@ -1,14 +1,32 @@
Change log for Android SDK Tools.
-Revision 11:
+Revision 14:
+
+- Build performance improvements:
+ * resource compilation and packaging now properly use dependency to only
+ be executed if a resource changed.
+ * Optimized resource compilation for projects with libraries. This should
+ speed up this phase significantly for large projects with libraries.
+ * PNG files that are optimized during resource packaging are now cached
+ and only re-optimized if they changed instead of doing at every build.
+- New library project mechanism:
+ *
+
+Revision 13:
+
+Revision 12: (07/2011):
+- The AVD manager and emulator can now use system images compiled for ARM v7 and
+ x86 CPUs.
+
+Revision 11 (05/2011):
- See eclipse/changes.txt for ADT related changes.
-Revision 10:
+Revision 10 (02/2011):
- The tools now automatically generate Java Programming Language
source files (in the gen directory) and bytecode (in the res/raw
directory) from your native .rs files
-Revision 9:
+Revision 9 (01/2011):
- Fix packaging issue that broke draw9patch
- Ant build rules will now check the Ant version and fail if it's older than 1.8
- Fix "folder locked" errors when installing packages in SDK Manager on Windows.
@@ -22,7 +40,7 @@ Revision 9:
- Logcat view in DDMS now properly displays UTF-8 characters.
-Revision 8:
+Revision 8 (12/2010):
- New SDK component: platform-tools. This makes all platforms use the same
latest version of aapt/aidl/dx.
- Support for true debug build. No need to change the value of debuggable in
@@ -36,7 +54,7 @@ Revision 8:
(default to "ascii", "1.5" and "1.5")
-Revision 7:
+Revision 7 (09/2010):
- Support for Ant rules provided by the Tools components (override the one in
the platform component)
- Added support for libraries with library dependencies.
diff --git a/eclipse/changes.txt b/eclipse/changes.txt
index 9d53db7..00a5a46 100644
--- a/eclipse/changes.txt
+++ b/eclipse/changes.txt
@@ -1,3 +1,9 @@
+13.0.0
+- Build system:
+ - ADT now uses bin/classes to output the java compilation
+ and bin/ for Android specific classes. This will make bin show up
+ in the package explorer.
+
12.0.0
- Many bug fixes!
- Visual Layout Editor:
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 4ab4f58..093ac9a 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
@@ -72,7 +72,7 @@ Export-Package: com.android,
com.android.ide.eclipse.adt.internal.build;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.build.builders;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.editors;x-friends:="com.android.ide.eclipse.tests",
- com.android.ide.eclipse.adt.internal.editors.binaryxml,
+ com.android.ide.eclipse.adt.internal.editors.binaryxml;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.editors.descriptors;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.editors.export;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.internal.editors.layout;x-friends:="com.android.ide.eclipse.tests",
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
index e828e51..a0b77b4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
@@ -392,11 +392,11 @@
<extension point="org.eclipse.jdt.core.classpathContainerInitializer">
<classpathContainerInitializer
class="com.android.ide.eclipse.adt.internal.project.AndroidClasspathContainerInitializer"
- id="com.android.ide.eclipse.adt.project.AndroidClasspathContainerInitializer">
+ id="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK">
</classpathContainerInitializer>
<classpathContainerInitializer
class="com.android.ide.eclipse.adt.internal.project.AndroidClasspathContainerInitializer"
- id="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK">
+ id="com.android.ide.eclipse.adt.LIBRARIES">
</classpathContainerInitializer>
</extension>
<extension point="org.eclipse.jdt.ui.classpathContainerPage">
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java
index ac2e4dd..e3ec83c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java
@@ -56,6 +56,14 @@ public class AdtConstants {
/** Nature of Android export projects */
public final static String NATURE_EXPORT = "com.android.ide.eclipse.adt.AndroidExportNature"; //$NON-NLS-1$
+ /** The container id for the android framework jar file */
+ public final static String CONTAINER_FRAMEWORK =
+ "com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"; //$NON-NLS-1$
+
+ /** The container id for the libraries */
+ public final static String CONTAINER_LIBRARIES = "com.android.ide.eclipse.adt.LIBRARIES"; //$NON-NLS-1$
+
+
/** Separator for workspace path, i.e. "/". */
public final static String WS_SEP = "/"; //$NON-NLS-1$
/** Separator character for workspace path, i.e. '/'. */
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
index 1231de3..af23185 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
@@ -33,6 +33,7 @@ import com.android.sdklib.build.ApkBuilder.JarStatus;
import com.android.sdklib.build.ApkBuilder.SigningInfo;
import com.android.sdklib.build.ApkCreationException;
import com.android.sdklib.build.DuplicateFileException;
+import com.android.sdklib.build.IArchiveBuilder;
import com.android.sdklib.build.SealedApkException;
import com.android.sdklib.internal.build.DebugKeyProvider;
import com.android.sdklib.internal.build.DebugKeyProvider.KeytoolException;
@@ -51,6 +52,7 @@ import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
@@ -135,7 +137,6 @@ public class BuildHelper {
mVerbose = verbose;
}
-
public void updateCrunchCache() throws AaptExecException, AaptResultException {
// Benchmarking start
long startCrunchTime = 0;
@@ -166,6 +167,12 @@ public class BuildHelper {
AdtPlugin.printBuildToConsole(BuildVerbosity.ALWAYS, mProject, msg);
}
}
+
+ public static void writeResources(IArchiveBuilder builder, IJavaProject javaProject)
+ throws DuplicateFileException, ApkCreationException, SealedApkException, CoreException {
+ writeStandardResources(builder, javaProject, null);
+ }
+
/**
* Packages the resources of the projet into a .ap_ file.
* @param manifestFile the manifest of the project.
@@ -881,7 +888,7 @@ public class BuildHelper {
* Writes the standard resources of a project and its referenced projects
* into a {@link SignedJarBuilder}.
* Standard resources are non java/aidl files placed in the java package folders.
- * @param apkBuilder the {@link ApkBuilder}.
+ * @param builder the archive builder.
* @param javaProject the javaProject object.
* @param referencedJavaProjects the java projects that this project references.
* @throws ApkCreationException if an error occurred
@@ -890,25 +897,24 @@ public class BuildHelper {
* at the same location inside the APK archive.
* @throws CoreException
*/
- private void writeStandardResources(ApkBuilder apkBuilder, IJavaProject javaProject,
+ private static void writeStandardResources(IArchiveBuilder builder, IJavaProject javaProject,
List<IJavaProject> referencedJavaProjects)
throws DuplicateFileException, ApkCreationException, SealedApkException,
CoreException {
IWorkspace ws = ResourcesPlugin.getWorkspace();
IWorkspaceRoot wsRoot = ws.getRoot();
- // create a list of path already put into the archive, in order to detect conflict
- ArrayList<String> list = new ArrayList<String>();
-
- writeStandardProjectResources(apkBuilder, javaProject, wsRoot, list);
+ writeStandardProjectResources(builder, javaProject, wsRoot);
- for (IJavaProject referencedJavaProject : referencedJavaProjects) {
- // only include output from non android referenced project
- // (This is to handle the case of reference Android projects in the context of
- // instrumentation projects that need to reference the projects to be tested).
- if (referencedJavaProject.getProject().hasNature(
- AdtConstants.NATURE_DEFAULT) == false) {
- writeStandardProjectResources(apkBuilder, referencedJavaProject, wsRoot, list);
+ if (referencedJavaProjects != null) {
+ for (IJavaProject referencedJavaProject : referencedJavaProjects) {
+ // only include output from non android referenced project
+ // (This is to handle the case of reference Android projects in the context of
+ // instrumentation projects that need to reference the projects to be tested).
+ if (referencedJavaProject.getProject().hasNature(
+ AdtConstants.NATURE_DEFAULT) == false) {
+ writeStandardProjectResources(builder, referencedJavaProject, wsRoot);
+ }
}
}
}
@@ -919,15 +925,14 @@ public class BuildHelper {
* @param jarBuilder the {@link ApkBuilder}.
* @param javaProject the javaProject object.
* @param wsRoot the {@link IWorkspaceRoot}.
- * @param list a list of files already added to the archive, to detect conflicts.
* @throws ApkCreationException if an error occurred
* @throws SealedApkException if the APK is already sealed.
* @throws DuplicateFileException if a file conflicts with another already added to the APK
* at the same location inside the APK archive.
* @throws CoreException
*/
- private void writeStandardProjectResources(ApkBuilder apkBuilder,
- IJavaProject javaProject, IWorkspaceRoot wsRoot, ArrayList<String> list)
+ private static void writeStandardProjectResources(IArchiveBuilder builder,
+ IJavaProject javaProject, IWorkspaceRoot wsRoot)
throws DuplicateFileException, ApkCreationException, SealedApkException, CoreException {
// get the source pathes
List<IPath> sourceFolders = BaseProjectHelper.getSourceClasspaths(javaProject);
@@ -936,14 +941,14 @@ public class BuildHelper {
for (IPath sourcePath : sourceFolders) {
IResource sourceResource = wsRoot.findMember(sourcePath);
if (sourceResource != null && sourceResource.getType() == IResource.FOLDER) {
- writeFolderResources(apkBuilder, javaProject, (IFolder) sourceResource);
+ writeFolderResources(builder, javaProject, (IFolder) sourceResource);
}
}
}
- private void writeFolderResources(ApkBuilder apkBuilder, final IJavaProject javaProject,
- IFolder root) throws CoreException, ApkCreationException,
- SealedApkException, DuplicateFileException {
+ private static void writeFolderResources(IArchiveBuilder builder,
+ final IJavaProject javaProject, IFolder root) throws CoreException,
+ ApkCreationException, SealedApkException, DuplicateFileException {
final List<IPath> pathsToPackage = new ArrayList<IPath>();
root.accept(new IResourceProxyVisitor() {
public boolean visit(IResourceProxy proxy) throws CoreException {
@@ -969,7 +974,7 @@ public class BuildHelper {
IPath rootLocation = root.getLocation();
for (IPath path : pathsToPackage) {
IPath archivePath = path.makeRelativeTo(rootLocation);
- apkBuilder.addFile(path.toFile(), archivePath.toString());
+ builder.addFile(path.toFile(), archivePath.toString());
}
}
@@ -987,60 +992,35 @@ public class BuildHelper {
IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
ArrayList<String> oslibraryList = new ArrayList<String>();
+
+ // we could use IJavaProject.getResolvedClasspath directly, but we actually
+ // want to see the containers themselves.
IClasspathEntry[] classpaths = javaProject.readRawClasspath();
if (classpaths != null) {
for (IClasspathEntry e : classpaths) {
- if (e.getEntryKind() == IClasspathEntry.CPE_LIBRARY ||
- e.getEntryKind() == IClasspathEntry.CPE_VARIABLE) {
- // if this is a classpath variable reference, we resolve it.
- if (e.getEntryKind() == IClasspathEntry.CPE_VARIABLE) {
- e = JavaCore.getResolvedClasspathEntry(e);
- }
-
- // get the IPath
- IPath path = e.getPath();
-
- IResource resource = wsRoot.findMember(path);
- // case of a jar file (which could be relative to the workspace or a full path)
- if (AdtConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) {
- if (resource != null && resource.exists() &&
- resource.getType() == IResource.FILE) {
- oslibraryList.add(resource.getLocation().toOSString());
- } else {
- // if the jar path doesn't match a workspace resource,
- // then we get an OSString and check if this links to a valid file.
- String osFullPath = path.toOSString();
-
- File f = new File(osFullPath);
- if (f.isFile()) {
- oslibraryList.add(osFullPath);
- } else {
- String message = String.format( Messages.Couldnt_Locate_s_Error,
- path);
- // always output to the console
- mOutStream.println(message);
-
- // put a marker
- if (resMarker != null) {
- resMarker.setWarning(mProject, message);
- }
- }
- }
- } else {
- // this can be the case for a class folder.
- if (resource != null && resource.exists() &&
- resource.getType() == IResource.FOLDER) {
- oslibraryList.add(resource.getLocation().toOSString());
- } else {
- // if the path doesn't match a workspace resource,
- // then we get an OSString and check if this links to a valid folder.
- String osFullPath = path.toOSString();
+ // if this is a classpath variable reference, we resolve it.
+ if (e.getEntryKind() == IClasspathEntry.CPE_VARIABLE) {
+ e = JavaCore.getResolvedClasspathEntry(e);
+ }
- File f = new File(osFullPath);
- if (f.isDirectory()) {
- oslibraryList.add(osFullPath);
+ if (e.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
+ handleClasspathEntry(e, wsRoot, oslibraryList, resMarker);
+ } else if (e.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
+ // get the container
+ try {
+ IClasspathContainer container = JavaCore.getClasspathContainer(
+ e.getPath(), javaProject);
+ // ignore the system and default_system types as they represent
+ // libraries that are part of the runtime.
+ if (container.getKind() == IClasspathContainer.K_APPLICATION) {
+ IClasspathEntry[] entries = container.getClasspathEntries();
+ for (IClasspathEntry entry : entries) {
+ handleClasspathEntry(entry, wsRoot, oslibraryList, resMarker);
}
}
+ } catch (JavaModelException jme) {
+ // can't resolve the container? ignore it.
+ AdtPlugin.log(jme, "Failed to resolve ClasspathContainer: %s", e.getPath());
}
}
}
@@ -1049,6 +1029,55 @@ public class BuildHelper {
return oslibraryList.toArray(new String[oslibraryList.size()]);
}
+ private void handleClasspathEntry(IClasspathEntry e, IWorkspaceRoot wsRoot,
+ ArrayList<String> oslibraryList, ResourceMarker resMarker) {
+ // get the IPath
+ IPath path = e.getPath();
+
+ IResource resource = wsRoot.findMember(path);
+ // case of a jar file (which could be relative to the workspace or a full path)
+ if (AdtConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) {
+ if (resource != null && resource.exists() &&
+ resource.getType() == IResource.FILE) {
+ oslibraryList.add(resource.getLocation().toOSString());
+ } else {
+ // if the jar path doesn't match a workspace resource,
+ // then we get an OSString and check if this links to a valid file.
+ String osFullPath = path.toOSString();
+
+ File f = new File(osFullPath);
+ if (f.isFile()) {
+ oslibraryList.add(osFullPath);
+ } else {
+ String message = String.format( Messages.Couldnt_Locate_s_Error,
+ path);
+ // always output to the console
+ mOutStream.println(message);
+
+ // put a marker
+ if (resMarker != null) {
+ resMarker.setWarning(mProject, message);
+ }
+ }
+ }
+ } else {
+ // this can be the case for a class folder.
+ if (resource != null && resource.exists() &&
+ resource.getType() == IResource.FOLDER) {
+ oslibraryList.add(resource.getLocation().toOSString());
+ } else {
+ // if the path doesn't match a workspace resource,
+ // then we get an OSString and check if this links to a valid folder.
+ String osFullPath = path.toOSString();
+
+ File f = new File(osFullPath);
+ if (f.isDirectory()) {
+ oslibraryList.add(osFullPath);
+ }
+ }
+ }
+ }
+
/**
* Returns the list of the output folders for the specified {@link IJavaProject} objects, if
* they are Android projects.
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java
index 15aedaf..07a2a25 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java
@@ -38,6 +38,7 @@ import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.build.ApkCreationException;
import com.android.sdklib.build.DuplicateFileException;
+import com.android.sdklib.build.SealedApkException;
import com.android.sdklib.internal.build.DebugKeyProvider.KeytoolException;
import org.eclipse.core.resources.IContainer;
@@ -58,9 +59,18 @@ import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.regex.Pattern;
public class PostCompilerBuilder extends BaseBuilder {
@@ -253,11 +263,14 @@ public class PostCompilerBuilder extends BaseBuilder {
try {
// get the project info
ProjectState projectState = Sdk.getProjectState(project);
- if (projectState == null || projectState.isLibrary()) {
- // library project do not need to be dexified or packaged.
+
+ // this can happen if the project has no default.properties.
+ if (projectState == null) {
return null;
}
+ boolean isLibrary = projectState.isLibrary();
+
// get the libraries
List<IProject> libProjects = projectState.getFullLibraryProjects();
@@ -294,6 +307,7 @@ public class PostCompilerBuilder extends BaseBuilder {
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project,
Messages.Start_Full_Apk_Build);
+ // Full build: we do all the steps.
mUpdateCrunchCache = true;
mPackageResources = true;
mConvertToDex = true;
@@ -305,6 +319,7 @@ public class PostCompilerBuilder extends BaseBuilder {
// go through the resources and see if something changed.
IResourceDelta delta = getDelta(project);
if (delta == null) {
+ // no delta? Same as full build: we do all the steps.
mUpdateCrunchCache = true;
mPackageResources = true;
mConvertToDex = true;
@@ -397,6 +412,25 @@ public class PostCompilerBuilder extends BaseBuilder {
Messages.Start_Full_Post_Compiler);
}
+ // finished with the common init and tests. Special case of the library.
+ if (isLibrary) {
+ // check the jar output file is present, if not create it.
+ IFile jarIFile = androidOutputFolder.getFile(
+ project.getName().toLowerCase() + AdtConstants.DOT_JAR);
+ if (mConvertToDex == false && jarIFile.exists() == false) {
+ mConvertToDex = true;
+ }
+
+ if (mConvertToDex) {
+ IFolder javaOutputFolder = BaseProjectHelper.getJavaOutputFolder(project);
+
+ writeLibraryPackage(jarIFile, project, javaOutputFolder,
+ referencedJavaProjects);
+ }
+
+ return allRefProjects;
+ }
+
// first thing we do is check that the SDK directory has been setup.
String osSdkFolder = AdtPlugin.getOsSdkFolder();
@@ -716,6 +750,141 @@ public class PostCompilerBuilder extends BaseBuilder {
return allRefProjects;
}
+ private static class JarBuilder implements IArchiveBuilder {
+
+ private static Pattern R_PATTERN = Pattern.compile("R(\\$.*)?\\.class"); //$NON-NLS-1$
+
+ private final byte[] buffer = new byte[1024];
+ private final JarOutputStream mOutputStream;
+
+ JarBuilder(JarOutputStream outputStream) {
+ mOutputStream = outputStream;
+ }
+
+ public void addFile(IFile file, IFolder rootFolder) throws ApkCreationException {
+ // we only package class file from the output folder
+ if (AdtConstants.EXT_CLASS.equals(file.getFileExtension()) == false) {
+ return;
+ }
+
+ // we don't package any R[$*] classes.
+ String name = file.getName();
+ if (R_PATTERN.matcher(name).matches()) {
+ return;
+ }
+
+ IPath path = file.getFullPath().makeRelativeTo(rootFolder.getFullPath());
+ try {
+ addFile(file.getContents(), file.getLocalTimeStamp(), path.toString());
+ } catch (ApkCreationException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new ApkCreationException(e, "Failed to add %s", file);
+ }
+ }
+
+ public void addFile(File file, String archivePath) throws ApkCreationException,
+ SealedApkException, DuplicateFileException {
+ try {
+ FileInputStream inputStream = new FileInputStream(file);
+ long lastModified = file.lastModified();
+ addFile(inputStream, lastModified, archivePath);
+ } catch (ApkCreationException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new ApkCreationException(e, "Failed to add %s", file);
+ }
+ }
+
+ private void addFile(InputStream content, long lastModified, String archivePath)
+ throws IOException, ApkCreationException {
+ // create the jar entry
+ JarEntry entry = new JarEntry(archivePath);
+ entry.setTime(lastModified);
+
+ try {
+ // add the entry to the jar archive
+ mOutputStream.putNextEntry(entry);
+
+ // read the content of the entry from the input stream, and write
+ // it into the archive.
+ int count;
+ while ((count = content.read(buffer)) != -1) {
+ mOutputStream.write(buffer, 0, count);
+ }
+ } finally {
+ try {
+ if (content != null) {
+ content.close();
+ }
+ } catch (Exception e) {
+ throw new ApkCreationException(e, "Failed to close stream");
+ }
+ }
+ }
+ }
+
+ private void writeLibraryPackage(IFile jarIFile, IProject project, IFolder javaOutputFolder,
+ List<IJavaProject> referencedJavaProjects) {
+
+ JarOutputStream jos = null;
+ try {
+ Manifest manifest = new Manifest();
+ Attributes mainAttributes = manifest.getMainAttributes();
+ mainAttributes.put(Attributes.Name.CLASS_PATH, "Android ADT"); //$NON-NLS-1$
+ mainAttributes.putValue("Created-By", "1.0 (Android)"); //$NON-NLS-1$ //$NON-NLS-2$
+ jos = new JarOutputStream(
+ new FileOutputStream(jarIFile.getLocation().toFile()), manifest);
+
+ JarBuilder jarBuilder = new JarBuilder(jos);
+
+ // write the class files
+ writeClassFilesIntoJar(jarBuilder, javaOutputFolder, javaOutputFolder);
+
+ // now write the standard Java resources
+ BuildHelper.writeResources(jarBuilder, JavaCore.create(project));
+
+ // do the same for all the referencedJava project
+ for (IJavaProject javaProject : referencedJavaProjects) {
+ IFolder refProjectOutput = BaseProjectHelper.getJavaOutputFolder(
+ javaProject.getProject());
+
+ if (refProjectOutput != null) {
+ // write the class files
+ writeClassFilesIntoJar(jarBuilder, refProjectOutput, refProjectOutput);
+
+ // now write the standard Java resources
+ BuildHelper.writeResources(jarBuilder, javaProject);
+ }
+ }
+
+ saveProjectBooleanProperty(PROPERTY_CONVERT_TO_DEX , mConvertToDex);
+ } catch (Exception e) {
+ AdtPlugin.log(e, "Failed to write jar file %s", jarIFile.getLocation().toOSString());
+ } finally {
+ if (jos != null) {
+ try {
+ jos.close();
+ } catch (IOException e) {
+ // pass
+ }
+ }
+ }
+ }
+
+ private void writeClassFilesIntoJar(JarBuilder builder, IFolder folder, IFolder rootFolder)
+ throws CoreException, IOException, ApkCreationException {
+ IResource[] members = folder.members();
+ for (IResource member : members) {
+ if (member.getType() == IResource.FOLDER) {
+ writeClassFilesIntoJar(builder, (IFolder) member, rootFolder);
+ } else if (member.getType() == IResource.FILE) {
+ IFile file = (IFile) member;
+ builder.addFile(file, rootFolder);
+ }
+ }
+ }
+
@Override
protected void startupOnInitialize() {
super.startupOnInitialize();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java
index 0163401..2a988d1 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java
@@ -493,7 +493,8 @@ public class PreCompilerBuilder extends BaseBuilder {
// generate resources.
boolean compiledTheResources = mMustCompileResources;
if (mMustCompileResources) {
- handleResources(project, javaPackage, projectTarget, manifestFile, libProjects);
+ handleResources(project, javaPackage, projectTarget, manifestFile, libProjects,
+ projectState.isLibrary());
saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES , false);
}
@@ -581,11 +582,13 @@ public class PreCompilerBuilder extends BaseBuilder {
* @param projectTarget the target of the main project
* @param manifest the {@link IFile} representing the project manifest
* @param libProjects the library dependencies
+ * @param isLibrary if the project is a library project
* @throws CoreException
* @throws AbortBuildException
*/
private void handleResources(IProject project, String javaPackage, IAndroidTarget projectTarget,
- IFile manifest, List<IProject> libProjects) throws CoreException, AbortBuildException {
+ IFile manifest, List<IProject> libProjects, boolean isLibrary)
+ throws CoreException, AbortBuildException {
// get the resource folder
IFolder resFolder = project.getFolder(AdtConstants.WS_RESOURCES);
@@ -636,12 +639,15 @@ public class PreCompilerBuilder extends BaseBuilder {
}
}
}
+
String libPackages = null;
if (libJavaPackages != null) {
libPackages = libJavaPackages.toString();
+
}
+
execAapt(project, projectTarget, osOutputPath, osResPath, osManifestPath,
- mainPackageFolder, libResFolders, libPackages);
+ mainPackageFolder, libResFolders, libPackages, isLibrary);
}
}
@@ -660,11 +666,13 @@ public class PreCompilerBuilder extends BaseBuilder {
* @param libResFolders the list of res folders for the library.
* @param libraryPackages an optional list of javapackages to replace the main project java package.
* can be null.
+ * @param isLibrary if the project is a library project
* @throws AbortBuildException
*/
private void execAapt(IProject project, IAndroidTarget projectTarget, String osOutputPath,
String osResPath, String osManifestPath, IFolder packageFolder,
- ArrayList<IFolder> libResFolders, String libraryPackages) throws AbortBuildException {
+ ArrayList<IFolder> libResFolders, String libraryPackages, boolean isLibrary)
+ throws AbortBuildException {
// 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.
@@ -680,6 +688,10 @@ public class PreCompilerBuilder extends BaseBuilder {
array.add("-v"); //$NON-NLS-1$
}
+ if (isLibrary) {
+ array.add("--non-constant-id"); //$NON-NLS-1$
+ }
+
if (libResFolders.size() > 0) {
array.add("--auto-add-overlay"); //$NON-NLS-1$
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutReloadMonitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutReloadMonitor.java
index ba77ce9..abc389f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutReloadMonitor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutReloadMonitor.java
@@ -18,8 +18,8 @@ package com.android.ide.eclipse.adt.internal.editors.layout;
import com.android.ide.common.resources.ResourceFile;
import com.android.ide.common.resources.ResourceFolder;
-import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IFileListener;
@@ -162,6 +162,9 @@ public final class LayoutReloadMonitor {
/**
* Implementation of the {@link IFileListener} as an internal class so that the methods
* do not appear in the public API of {@link LayoutReloadMonitor}.
+ *
+ * This is only to detect code and manifest change. Resource changes (located in res/)
+ * is done through {@link #mResourceListener}.
*/
private IFileListener mFileListener = new IFileListener() {
/*
@@ -183,8 +186,6 @@ public final class LayoutReloadMonitor {
if (hasAndroidNature) {
// project is an Android project, it's the one being affected
// directly by its own file change.
- // Note that resource change is handled separately, so there's no need to
- // figure out if the project is a library and to update its main project(s).
processFileChanged(file, project);
} else {
// check the projects depending on it, if they are Android project, update them.
@@ -248,7 +249,6 @@ public final class LayoutReloadMonitor {
}
changeFlags.manifest = true;
-
}
}
};
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainer.java
index bbef6b9..0b891c9 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainer.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainer.java
@@ -22,26 +22,31 @@ import org.eclipse.jdt.core.IClasspathEntry;
/**
* Classpath container for the Android projects.
+ * This supports both the System classpath and the library dependencies.
*/
class AndroidClasspathContainer implements IClasspathContainer {
-
- private IClasspathEntry[] mClasspathEntry;
- private IPath mContainerPath;
- private String mName;
-
+
+ private final IClasspathEntry[] mClasspathEntry;
+ private final IPath mContainerPath;
+ private final String mName;
+ private final int mKind;
+
/**
* Constructs the container with the {@link IClasspathEntry} representing the android
* framework jar file and the container id
* @param entries the entries representing the android framework and optional libraries.
* @param path the path containing the classpath container id.
* @param name the name of the container to display.
+ * @param the container kind. Can be {@link IClasspathContainer#K_DEFAULT_SYSTEM} or
+ * {@link IClasspathContainer#K_APPLICATION}
*/
- AndroidClasspathContainer(IClasspathEntry[] entries, IPath path, String name) {
+ AndroidClasspathContainer(IClasspathEntry[] entries, IPath path, String name, int kind) {
mClasspathEntry = entries;
mContainerPath = path;
mName = name;
+ mKind = kind;
}
-
+
public IClasspathEntry[] getClasspathEntries() {
return mClasspathEntry;
}
@@ -51,7 +56,7 @@ class AndroidClasspathContainer implements IClasspathContainer {
}
public int getKind() {
- return IClasspathContainer.K_DEFAULT_SYSTEM;
+ return mKind;
}
public IPath getPath() {
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 9ae60c0..d83d539 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
@@ -17,8 +17,8 @@
package com.android.ide.eclipse.adt.internal.project;
import com.android.ide.common.sdk.LoadStatus;
-import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.sdklib.AndroidVersion;
@@ -81,10 +81,6 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
private final static String PROPERTY_ANDROID_SOURCE = "androidSource"; //$NON-NLS-1$
- /** The container id for the android framework jar file */
- public final static String CONTAINER_ID =
- "com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"; //$NON-NLS-1$
-
/** path separator to store multiple paths in a single property. This is guaranteed to not
* be in a path.
*/
@@ -113,10 +109,10 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
*/
@Override
public void initialize(IPath containerPath, IJavaProject project) throws CoreException {
- if (CONTAINER_ID.equals(containerPath.toString())) {
+ if (AdtConstants.CONTAINER_FRAMEWORK.equals(containerPath.toString())) {
IClasspathContainer container = allocateAndroidContainer(project);
if (container != null) {
- JavaCore.setClasspathContainer(new Path(CONTAINER_ID),
+ JavaCore.setClasspathContainer(new Path(AdtConstants.CONTAINER_FRAMEWORK),
new IJavaProject[] { project },
new IClasspathContainer[] { container },
new NullProgressMonitor());
@@ -125,23 +121,6 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
}
/**
- * Creates a new {@link IClasspathEntry} of type {@link IClasspathEntry#CPE_CONTAINER}
- * linking to the Android Framework.
- */
- public static IClasspathEntry getContainerEntry() {
- return JavaCore.newContainerEntry(new Path(CONTAINER_ID));
- }
-
- /**
- * Checks the {@link IPath} objects against the android framework container id and
- * returns <code>true</code> if they are identical.
- * @param path the <code>IPath</code> to check.
- */
- public static boolean checkPath(IPath path) {
- return CONTAINER_ID.equals(path.toString());
- }
-
- /**
* Updates the {@link IJavaProject} objects with new android framework container. This forces
* JDT to recompile them.
* @param androidProjects the projects to update.
@@ -164,7 +143,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
// give each project their new container in one call.
JavaCore.setClasspathContainer(
- new Path(CONTAINER_ID),
+ new Path(AdtConstants.CONTAINER_FRAMEWORK),
androidProjects, containers, new NullProgressMonitor());
return true;
@@ -214,7 +193,9 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
return new AndroidClasspathContainer(
createClasspathEntries(iProject, target, targetName),
- new Path(CONTAINER_ID), targetName);
+ new Path(AdtConstants.CONTAINER_FRAMEWORK),
+ targetName,
+ IClasspathContainer.K_DEFAULT_SYSTEM);
}
// In case of error, we'll try different thing to provide the best error message
@@ -459,7 +440,8 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
IClasspathEntry[] entries = createClasspathEntriesFromPaths(paths, target);
return new AndroidClasspathContainer(entries,
- new Path(CONTAINER_ID), targetNameCache);
+ new Path(AdtConstants.CONTAINER_FRAMEWORK),
+ targetNameCache, IClasspathContainer.K_DEFAULT_SYSTEM);
}
/**
@@ -520,17 +502,16 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
// create the java doc link.
String androidApiURL = ProjectHelper.loadStringProperty(root, PROPERTY_ANDROID_API);
String apiURL = null;
- if (androidApiURL != null) {
+ if (androidApiURL != null && testURL(androidApiURL)) {
apiURL = androidApiURL;
} else {
- if (testURL(androidApiURL)) {
- apiURL = androidApiURL;
- } else if (testURL(paths[CACHE_INDEX_DOCS_URI])) {
+ if (testURL(paths[CACHE_INDEX_DOCS_URI])) {
apiURL = paths[CACHE_INDEX_DOCS_URI];
} else if (testURL(ANDROID_API_REFERENCE)) {
apiURL = ANDROID_API_REFERENCE;
}
}
+
IClasspathAttribute[] attributes = null;
if (apiURL != null && !NULL_API_URL.equals(apiURL)) {
IClasspathAttribute cpAttribute = JavaCore.newClasspathAttribute(
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerPage.java
index 4641c22..ed4e1ed 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerPage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerPage.java
@@ -17,6 +17,8 @@
package com.android.ide.eclipse.adt.internal.project;
+import com.android.ide.eclipse.adt.AdtConstants;
+
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
@@ -61,7 +63,7 @@ public class AndroidClasspathContainerPage extends WizardPage implements IClassp
}
public IClasspathEntry getSelection() {
- IPath path = new Path(AndroidClasspathContainerInitializer.CONTAINER_ID);
+ IPath path = new Path(AdtConstants.CONTAINER_FRAMEWORK);
final int index = this.mProjectsCombo.getSelectionIndex();
if (index != -1) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/FolderDecorator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/FolderDecorator.java
index 4fd3c35..2ca4269 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/FolderDecorator.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/FolderDecorator.java
@@ -18,7 +18,6 @@ package com.android.ide.eclipse.adt.internal.project;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.sdklib.SdkConstants;
import org.eclipse.core.resources.IFolder;
@@ -68,9 +67,6 @@ public class FolderDecorator implements ILightweightLabelDecorator {
doDecoration(decoration, null);
} else if (name.equals(SdkConstants.FD_OUTPUT)) {
doDecoration(decoration, null);
- } else if (folder.isLinked() && Sdk.CREATOR_ADT.equals(
- ProjectHelper.loadStringProperty(folder, Sdk.PROP_CREATOR))) {
- doDecoration(decoration, " [Android Library]");
}
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/LibraryClasspathContainerInitializer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/LibraryClasspathContainerInitializer.java
new file mode 100644
index 0000000..3d0d988
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/LibraryClasspathContainerInitializer.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.project;
+
+import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.ClasspathContainerInitializer;
+import org.eclipse.jdt.core.IClasspathContainer;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LibraryClasspathContainerInitializer extends ClasspathContainerInitializer {
+
+ public LibraryClasspathContainerInitializer() {
+ }
+
+ /**
+ * Updates the {@link IJavaProject} objects with new library.
+ * @param androidProjects the projects to update.
+ * @return <code>true</code> if success, <code>false</code> otherwise.
+ */
+ public static boolean updateProjects(IJavaProject[] androidProjects) {
+ try {
+ // Allocate a new AndroidClasspathContainer, and associate it to the library
+ // container id for each projects.
+ int projectCount = androidProjects.length;
+
+ IClasspathContainer[] containers = new IClasspathContainer[projectCount];
+ for (int i = 0 ; i < projectCount; i++) {
+ containers[i] = allocateAndroidContainer(androidProjects[i]);
+ }
+
+ // give each project their new container in one call.
+ JavaCore.setClasspathContainer(
+ new Path(AdtConstants.CONTAINER_LIBRARIES),
+ androidProjects, containers, new NullProgressMonitor());
+
+ return true;
+ } catch (JavaModelException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public void initialize(IPath containerPath, IJavaProject project) throws CoreException {
+ if (AdtConstants.CONTAINER_LIBRARIES.equals(containerPath.toString())) {
+ IClasspathContainer container = allocateAndroidContainer(project);
+ if (container != null) {
+ JavaCore.setClasspathContainer(new Path(AdtConstants.CONTAINER_LIBRARIES),
+ new IJavaProject[] { project },
+ new IClasspathContainer[] { container },
+ new NullProgressMonitor());
+ }
+ }
+ }
+
+ private static IClasspathContainer allocateAndroidContainer(IJavaProject javaProject) {
+ final IProject iProject = javaProject.getProject();
+
+ AdtPlugin plugin = AdtPlugin.getDefault();
+ if (plugin == null) { // This is totally weird, but I've seen it happen!
+ return null;
+ }
+
+ // check if the project has a valid target.
+ ProjectState state = Sdk.getProjectState(iProject);
+
+ List<IClasspathEntry> entries = new ArrayList<IClasspathEntry>();
+
+ List<IProject> libProjects = state.getFullLibraryProjects();
+ for (IProject libProject : libProjects) {
+ // get the project output
+ IFolder outputFolder = BaseProjectHelper.getAndroidOutputFolder(libProject);
+
+ IFile jarIFile = outputFolder.getFile(libProject.getName().toLowerCase() +
+ AdtConstants.DOT_JAR);
+
+ IClasspathEntry entry = JavaCore.newLibraryEntry(
+ jarIFile.getLocation(),
+ libProject.getLocation(), // source attachment path
+ null); // default source attachment root path.
+
+ entries.add(entry);
+ }
+
+ return new AndroidClasspathContainer(
+ entries.toArray(new IClasspathEntry[entries.size()]),
+ new Path(AdtConstants.CONTAINER_LIBRARIES),
+ "Library Projects",
+ IClasspathContainer.K_APPLICATION);
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java
index 2804bac..428e8d9 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java
@@ -268,7 +268,8 @@ public final class ProjectHelper {
// get the output folder
IPath outputFolder = javaProject.getOutputLocation();
- boolean foundContainer = false;
+ boolean foundFrameworkContainer = false;
+ boolean foundLibrariesContainer = false;
for (int i = 0 ; i < entries.length ;) {
// get the entry and kind
@@ -285,8 +286,12 @@ public final class ProjectHelper {
continue;
}
} else if (kind == IClasspathEntry.CPE_CONTAINER) {
- if (AndroidClasspathContainerInitializer.checkPath(entry.getPath())) {
- foundContainer = true;
+ String path = entry.getPath().toString();
+ if (AdtConstants.CONTAINER_FRAMEWORK.equals(path)) {
+ foundFrameworkContainer = true;
+ }
+ if (AdtConstants.CONTAINER_LIBRARIES.equals(path)) {
+ foundLibrariesContainer = true;
}
}
@@ -294,10 +299,17 @@ public final class ProjectHelper {
}
// if the framework container is not there, we add it
- if (foundContainer == false) {
+ if (foundFrameworkContainer == false) {
+ // add the android container to the array
+ entries = ProjectHelper.addEntryToClasspath(entries,
+ JavaCore.newContainerEntry(new Path(AdtConstants.CONTAINER_FRAMEWORK)));
+ }
+
+ // same thing for the library container
+ if (foundLibrariesContainer == false) {
// add the android container to the array
entries = ProjectHelper.addEntryToClasspath(entries,
- AndroidClasspathContainerInitializer.getContainerEntry());
+ JavaCore.newContainerEntry(new Path(AdtConstants.CONTAINER_LIBRARIES)));
}
// set the new list of entries to the project
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/GlobalProjectMonitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/GlobalProjectMonitor.java
index f2e6485..6e9881f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/GlobalProjectMonitor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/GlobalProjectMonitor.java
@@ -382,9 +382,14 @@ public final class GlobalProjectMonitor {
IJavaProject[] androidProjects = BaseProjectHelper.getAndroidProjects(javaModel,
null /*filter*/);
+
+ notifyResourceEventStart();
+
for (IJavaProject androidProject : androidProjects) {
listener.projectOpenedWithWorkspace(androidProject.getProject());
}
+
+ notifyResourceEventEnd();
}
/**
@@ -427,7 +432,27 @@ public final class GlobalProjectMonitor {
mRawDeltaListeners.remove(listener);
}
- private final IResourceChangeListener mResourceChangeListener = new IResourceChangeListener() {
+ private void notifyResourceEventStart() {
+ for (IResourceEventListener listener : mEventListeners) {
+ try {
+ listener.resourceChangeEventStart();
+ } catch (Throwable t) {
+ AdtPlugin.log(t,"Failed to call IResourceEventListener.resourceChangeEventStart");
+ }
+ }
+ }
+
+ private void notifyResourceEventEnd() {
+ for (IResourceEventListener listener : mEventListeners) {
+ try {
+ listener.resourceChangeEventEnd();
+ } catch (Throwable t) {
+ AdtPlugin.log(t,"Failed to call IResourceEventListener.resourceChangeEventEnd");
+ }
+ }
+ }
+
+ private IResourceChangeListener mResourceChangeListener = new IResourceChangeListener() {
/**
* Processes the workspace resource change events.
*
@@ -435,13 +460,7 @@ public final class GlobalProjectMonitor {
*/
public synchronized void resourceChanged(IResourceChangeEvent event) {
// notify the event listeners of a start.
- for (IResourceEventListener listener : mEventListeners) {
- try {
- listener.resourceChangeEventStart();
- } catch (Throwable t) {
- AdtPlugin.log(t,"Failed to call IResourceEventListener.resourceChangeEventStart");
- }
- }
+ notifyResourceEventStart();
if (event.getType() == IResourceChangeEvent.PRE_DELETE) {
// a project is being deleted. Lets get the project object and remove
@@ -469,13 +488,7 @@ public final class GlobalProjectMonitor {
}
// we're done, notify the event listeners.
- for (IResourceEventListener listener : mEventListeners) {
- try {
- listener.resourceChangeEventEnd();
- } catch (Throwable t) {
- AdtPlugin.log(t,"Failed to call IResourceEventListener.resourceChangeEventEnd");
- }
- }
+ notifyResourceEventEnd();
}
};
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java
index dc543b8..4495553 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java
@@ -18,7 +18,8 @@ package com.android.ide.eclipse.adt.internal.resources.manager;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.build.BuildHelper;
-import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
+import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
@@ -74,17 +75,24 @@ public final class ProjectClassLoader extends ClassLoader {
return clazz;
}
- // attempt to load the class from the referenced projects.
+ // attempt to load the class from the libraries
try {
- List<IProject> javaProjects = ProjectHelper.getReferencedProjects(
- mJavaProject.getProject());
- List<IJavaProject> referencedJavaProjects = BuildHelper.getJavaProjects(javaProjects);
+ // get the project info
+ ProjectState projectState = Sdk.getProjectState(mJavaProject.getProject());
- for (IJavaProject javaProject : referencedJavaProjects) {
- clazz = loadFromProject(javaProject, name);
+ // this can happen if the project has no default.properties.
+ if (projectState != null) {
- if (clazz != null) {
- return clazz;
+ List<IProject> libProjects = projectState.getFullLibraryProjects();
+ List<IJavaProject> referencedJavaProjects = BuildHelper.getJavaProjects(
+ libProjects);
+
+ for (IJavaProject javaProject : referencedJavaProjects) {
+ clazz = loadFromProject(javaProject, name);
+
+ if (clazz != null) {
+ return clazz;
+ }
}
}
} catch (CoreException e) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/ProjectState.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/ProjectState.java
index d57cdaf..5b221fc 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/ProjectState.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/ProjectState.java
@@ -149,7 +149,7 @@ public final class ProjectState {
@Override
public int hashCode() {
- return mRelativePath.hashCode();
+ return normalizePath(mRelativePath).hashCode();
}
}
@@ -554,6 +554,10 @@ public final class ProjectState {
mParentProjects.remove(parentState);
}
+ public List<ProjectState> getParentProjects() {
+ return Collections.unmodifiableList(mParentProjects);
+ }
+
/**
* Update the value of a library dependency.
* <p/>This loops on all current dependency looking for the value to replace and then replaces
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 0002668..6d476d9 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
@@ -24,7 +24,7 @@ import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.build.DexWrapper;
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.ProjectHelper;
+import com.android.ide.eclipse.adt.internal.project.LibraryClasspathContainerInitializer;
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;
@@ -46,22 +46,15 @@ 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.IWorkspaceRoot;
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;
@@ -70,7 +63,6 @@ import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -88,11 +80,9 @@ import java.util.Map.Entry;
* To get the list of platforms or add-ons present in the SDK, call {@link #getTargets()}.
*/
public final class Sdk {
- private static final String PROP_LIBRARY = "_library"; //$NON-NLS-1$
- private static final String PROP_LIBRARY_NAME = "_library_name"; //$NON-NLS-1$
- public static final String CREATOR_ADT = "ADT"; //$NON-NLS-1$
- public static final String PROP_CREATOR = "_creator"; //$NON-NLS-1$
- private final static Object sLock = new Object();
+ private final static boolean DEBUG = false;
+
+ private final static Object LOCK = new Object();
private static Sdk sCurrentSdk = null;
@@ -198,7 +188,7 @@ public final class Sdk {
* projects.
*/
public static final Object getLock() {
- return sLock;
+ return LOCK;
}
/**
@@ -207,7 +197,7 @@ public final class Sdk {
* @param sdkLocation the OS path to the SDK.
*/
public static Sdk loadSdk(String sdkLocation) {
- synchronized (sLock) {
+ synchronized (LOCK) {
if (sCurrentSdk != null) {
sCurrentSdk.dispose();
sCurrentSdk = null;
@@ -273,7 +263,7 @@ public final class Sdk {
* Returns the current {@link Sdk} object.
*/
public static Sdk getCurrent() {
- synchronized (sLock) {
+ synchronized (LOCK) {
return sCurrentSdk;
}
}
@@ -326,7 +316,7 @@ public final class Sdk {
return;
}
- synchronized (sLock) {
+ synchronized (LOCK) {
// check if there's already a state?
ProjectState state = getProjectState(project);
@@ -369,7 +359,7 @@ public final class Sdk {
return null;
}
- synchronized (sLock) {
+ synchronized (LOCK) {
ProjectState state = sProjectStateMap.get(project);
if (state == null) {
// load the default.properties from the project folder.
@@ -449,7 +439,7 @@ public final class Sdk {
public LoadStatus checkAndLoadTargetData(final IAndroidTarget target, IJavaProject project) {
boolean loadData = false;
- synchronized (sLock) {
+ synchronized (LOCK) {
if (mDontLoadTargetData) {
return LoadStatus.FAILED;
}
@@ -491,7 +481,7 @@ public final class Sdk {
IJavaProject[] javaProjectArray = null;
- synchronized (sLock) {
+ synchronized (LOCK) {
TargetLoadBundle bundle = mTargetDataStatusMap.get(target);
if (status.getCode() != IStatus.OK) {
@@ -516,7 +506,7 @@ public final class Sdk {
return status;
} catch (Throwable t) {
- synchronized (sLock) {
+ synchronized (LOCK) {
TargetLoadBundle bundle = mTargetDataStatusMap.get(target);
bundle.status = LoadStatus.FAILED;
}
@@ -543,7 +533,7 @@ public final class Sdk {
* Return the {@link AndroidTargetData} for a given {@link IAndroidTarget}.
*/
public AndroidTargetData getTargetData(IAndroidTarget target) {
- synchronized (sLock) {
+ synchronized (LOCK) {
return mTargetDataMap.get(target);
}
}
@@ -552,7 +542,7 @@ public final class Sdk {
* Return the {@link AndroidTargetData} for a given {@link IProject}.
*/
public AndroidTargetData getTargetData(IProject project) {
- synchronized (sLock) {
+ synchronized (LOCK) {
IAndroidTarget target = getTarget(project);
if (target != null) {
return getTargetData(target);
@@ -604,7 +594,7 @@ public final class Sdk {
* @return a possibly empty list of ProjectState.
*/
public static Set<ProjectState> getMainProjectsFor(IProject project) {
- synchronized (sLock) {
+ synchronized (LOCK) {
// first get the project directly depending on this.
HashSet<ProjectState> list = new HashSet<ProjectState>();
@@ -645,7 +635,7 @@ public final class Sdk {
* this {@link Sdk} instance.
*/
public void unloadTargetData(boolean preventReload) {
- synchronized (sLock) {
+ synchronized (LOCK) {
mDontLoadTargetData = preventReload;
// dispose of the target data.
@@ -664,9 +654,11 @@ public final class Sdk {
// listen to projects closing
GlobalProjectMonitor monitor = GlobalProjectMonitor.getMonitor();
+ // need to register the resource event listener first because the project listener
+ // is called back during registration with project opened in the workspace.
+ monitor.addResourceEventListener(mResourceEventListener);
monitor.addProjectListener(mProjectListener);
monitor.addFileListener(mFileListener, IResourceDelta.CHANGED | IResourceDelta.ADDED);
- monitor.addResourceEventListener(mResourceEventListener);
// pre-compute some paths
mDocBaseUrl = getDocumentationBaseUrl(mManager.getLocation() +
@@ -678,7 +670,7 @@ public final class Sdk {
loadLayoutDevices();
// update whatever ProjectState is already present with new IAndroidTarget objects.
- synchronized (sLock) {
+ synchronized (LOCK) {
for (Entry<IProject, ProjectState> entry: sProjectStateMap.entrySet()) {
entry.getValue().setTarget(
getTargetFromHashString(entry.getValue().getTargetHashString()));
@@ -695,8 +687,8 @@ public final class Sdk {
monitor.removeFileListener(mFileListener);
monitor.removeResourceEventListener(mResourceEventListener);
- synchronized (sLock) {
- // the IAndroidTarget objects are now obsolete so update the project states.
+ // the IAndroidTarget objects are now obsolete so update the project states.
+ synchronized (LOCK) {
for (Entry<IProject, ProjectState> entry: sProjectStateMap.entrySet()) {
entry.getValue().setTarget(null);
}
@@ -711,7 +703,7 @@ public final class Sdk {
}
void setTargetData(IAndroidTarget target, AndroidTargetData data) {
- synchronized (sLock) {
+ synchronized (LOCK) {
mTargetDataMap.put(target, data);
}
}
@@ -783,9 +775,9 @@ public final class Sdk {
onProjectRemoved(project, true /*deleted*/);
}
- private void onProjectRemoved(IProject project, boolean deleted) {
+ private void onProjectRemoved(IProject removedProject, boolean deleted) {
try {
- if (project.hasNature(AdtConstants.NATURE_DEFAULT) == false) {
+ if (removedProject.hasNature(AdtConstants.NATURE_DEFAULT) == false) {
return;
}
} catch (CoreException e) {
@@ -794,71 +786,59 @@ public final class Sdk {
// which is processed before the project is actually removed/closed.
}
+ if (DEBUG) {
+ System.out.println(">>> CLOSED: " + removedProject.getName());
+ }
+
// get the target project
- synchronized (sLock) {
+ synchronized (LOCK) {
// Don't use getProject() as it could create the ProjectState if it's not
// there yet and this is not what we want. We want the current object.
// Therefore, direct access to the map.
- ProjectState state = sProjectStateMap.get(project);
- if (state != null) {
+ ProjectState removedState = sProjectStateMap.get(removedProject);
+ if (removedState != null) {
// 1. clear the layout lib cache associated with this project
- IAndroidTarget target = state.getTarget();
+ IAndroidTarget target = removedState.getTarget();
if (target != null) {
// get the bridge for the target, and clear the cache for this project.
AndroidTargetData data = mTargetDataMap.get(target);
if (data != null) {
LayoutLibrary layoutLib = data.getLayoutLibrary();
if (layoutLib != null && layoutLib.getStatus() == LoadStatus.LOADED) {
- layoutLib.clearCaches(project);
+ layoutLib.clearCaches(removedProject);
}
}
}
// 2. if the project is a library, make sure to update the
- // LibraryState for any main project using this.
+ // LibraryState for any project referencing it.
// Also, record the updated projects that are libraries, to update
// projects that depend on them.
- ArrayList<ProjectState> updatedLibraries = new ArrayList<ProjectState>();
for (ProjectState projectState : sProjectStateMap.values()) {
- LibraryState libState = projectState.getLibrary(project);
+ LibraryState libState = projectState.getLibrary(removedProject);
if (libState != null) {
- // get the current libraries.
- List<IProject> oldLibraries = projectState.getFullLibraryProjects();
-
- // the unlink below will work in the job, but we need to close
- // the library right away.
+ // Close the library right away.
+ // This remove links between the LibraryState and the projectState.
// This is because in case of a rename of a project, projectClosed and
// projectOpened will be called before any other job is run, so we
// need to make sure projectOpened is closed with the main project
// state up to date.
libState.close();
-
- // edit the project to remove the linked source folder.
- // this also calls LibraryState.close();
- LinkUpdateBundle bundle = getLinkBundle(projectState, oldLibraries);
- if (bundle != null) {
- queueLinkUpdateBundle(bundle);
- }
-
- if (projectState.isLibrary()) {
- updatedLibraries.add(projectState);
- }
+ // record that this project changed, and in case it's a library
+ // that its parents need to be updated as well.
+ markProject(projectState, projectState.isLibrary());
}
}
- if (deleted) {
- // remove the linked path variable
- disposeLibraryProject(project);
- }
-
// now remove the project for the project map.
- sProjectStateMap.remove(project);
-
- // update the projects that depend on the updated project
- updateProjectsWithNewLibraries(updatedLibraries);
+ sProjectStateMap.remove(removedProject);
}
}
+
+ if (DEBUG) {
+ System.out.println("<<<");
+ }
}
public void projectOpened(IProject project) {
@@ -916,115 +896,70 @@ public final class Sdk {
ProjectState openedState = getProjectState(openedProject);
if (openedState != null) {
- if (openedState.hasLibraries()) {
- // list of library to link to the opened project.
- final ArrayList<IProject> libsToLink = new ArrayList<IProject>();
+ if (DEBUG) {
+ System.out.println(">>> OPENED: " + openedProject.getName());
+ }
- // Look for all other opened projects to see if any is a library for the opened
- // project.
- synchronized (sLock) {
+ synchronized (LOCK) {
+ final boolean isLibrary = openedState.isLibrary();
+ final boolean hasLibraries = openedState.hasLibraries();
+
+ if (isLibrary || hasLibraries) {
+ boolean foundLibraries = false;
+ // loop on all the existing project and update them based on this new
+ // project
for (ProjectState projectState : sProjectStateMap.values()) {
if (projectState != openedState) {
- // ProjectState#needs() both checks if this is a missing library
- // and updates LibraryState to contains the new values.
- LibraryState libState = openedState.needs(projectState);
-
- if (libState != null) {
- // we have a match! Add the library to the list (if it was
- // not added through an indirect dependency before).
- IProject libProject = libState.getProjectState().getProject();
- if (libsToLink.contains(libProject) == false) {
- libsToLink.add(libProject);
+ // If the project has libraries, check if this project
+ // is a reference.
+ if (hasLibraries) {
+ // ProjectState#needs() both checks if this is a missing library
+ // and updates LibraryState to contains the new values.
+ // This must always be called.
+ LibraryState libState = openedState.needs(projectState);
+
+ if (libState != null) {
+ // found a library! Add the main project to the list of
+ // modified project
+ foundLibraries = true;
}
+ }
- // now find what this depends on, and add it too.
- // The order here doesn't matter
- // as it's just to add the linked source folder, so there's no
- // need to use ProjectState#getFullLibraryProjects() which
- // could return project that have already been added anyway.
- fillProjectDependenciesList(libState.getProjectState(),
- libsToLink);
+ // if the project is a library check if the other project depend
+ // on it.
+ if (isLibrary) {
+ // ProjectState#needs() both checks if this is a missing library
+ // and updates LibraryState to contains the new values.
+ // This must always be called.
+ LibraryState libState = projectState.needs(openedState);
+
+ if (libState != null) {
+ // There's a dependency! Add the project to the list of
+ // modified project, but also to a list of projects
+ // that saw one of its dependencies resolved.
+ markProject(projectState, projectState.isLibrary());
+ }
}
}
}
- }
- // create a link bundle always, because even if there's no libraries to add
- // to the CPE, the cleaning of invalid CPE must happen.
- LinkUpdateBundle bundle = new LinkUpdateBundle();
- bundle.mProject = openedProject;
- bundle.mNewLibraryProjects = libsToLink.toArray(
- new IProject[libsToLink.size()]);
- bundle.mCleanupCPE = true;
- queueLinkUpdateBundle(bundle);
+ // if the project has a libraries and we found at least one, we add
+ // the project to the list of modified project.
+ // Since we already went through the parent, no need to update them.
+ if (foundLibraries) {
+ markProject(openedState, false /*updateParents*/);
+ }
+ }
}
- // if the project is a library, then add it to the list of projects being opened.
- // They will be processed in IResourceEventListener#resourceChangeEventEnd.
- // This is done so that we are sure to process all the projects being opened
- // first and only then process projects depending on the projects that were opened.
- if (openedState.isLibrary()) {
- setupLibraryProject(openedProject);
-
- mOpenedLibraryProjects.add(openedState);
+ if (DEBUG) {
+ System.out.println("<<<");
}
}
}
public void projectRenamed(IProject project, IPath from) {
- try {
- if (project.hasNature(AdtConstants.NATURE_DEFAULT) == false) {
- return;
- }
- } catch (CoreException e) {
- // this can only happen if the project does not exist or is not open, neither
- // of which can happen here since we're processing a Project renamed event.
- }
-
- // a project was renamed.
- // if the project is a library, look for any project that depended on it
- // and update it. (default.properties and linked source folder)
- ProjectState renamedState = getProjectState(project);
- if (renamedState.isLibrary()) {
- // remove the variable
- disposeLibraryProject(from.lastSegment());
-
- // update the project depending on the library
- synchronized (sLock) {
- for (ProjectState projectState : sProjectStateMap.values()) {
- if (projectState != renamedState && projectState.isMissingLibraries()) {
- IPath oldRelativePath = from.makeRelativeTo(
- projectState.getProject().getFullPath());
-
- IPath newRelativePath = project.getFullPath().makeRelativeTo(
- projectState.getProject().getFullPath());
-
- // get the current libraries
- List<IProject> oldLibraries = projectState.getFullLibraryProjects();
-
- // update the library for the main project.
- LibraryState libState = projectState.updateLibrary(
- oldRelativePath.toString(), newRelativePath.toString(),
- renamedState);
- if (libState != null) {
- // this project depended on the renamed library, create a bundle
- // with the whole library difference (in case the renamed library
- // also depends on libraries).
-
- LinkUpdateBundle bundle = getLinkBundle(projectState,
- oldLibraries);
- queueLinkUpdateBundle(bundle);
-
- // add it to the opened projects to update whatever depends
- // on it
- if (projectState.isLibrary()) {
- mOpenedLibraryProjects.add(projectState);
- }
- }
- }
- }
- }
- }
+ // we don't actually care about this anymore.
}
};
@@ -1052,23 +987,15 @@ public final class Sdk {
// get the current library flag
boolean wasLibrary = state.isLibrary();
- // get the current list of project dependencies
- List<IProject> oldLibraries = state.getFullLibraryProjects();
-
LibraryDifference diff = state.reloadProperties();
// load the (possibly new) target.
IAndroidTarget newTarget = loadTarget(state);
- // check if this is a new library
- if (state.isLibrary() && wasLibrary == false) {
- setupLibraryProject(iProject);
- }
-
// reload the libraries if needed
if (diff.hasDiff()) {
if (diff.added) {
- synchronized (sLock) {
+ synchronized (LOCK) {
for (ProjectState projectState : sProjectStateMap.values()) {
if (projectState != state) {
// need to call needs to do the libraryState link,
@@ -1081,16 +1008,7 @@ public final class Sdk {
}
}
- // and build the real difference. A list of new projects and a list of
- // removed project.
- // This is not the same as the added/removed libraries because libraries
- // could be indirect dependencies through several different direct
- // dependencies so it's easier to compare the full lists before and after
- // the reload.
- LinkUpdateBundle bundle = getLinkBundle(state, oldLibraries);
- if (bundle != null) {
- queueLinkUpdateBundle(bundle);
- }
+ markProject(state, wasLibrary || state.isLibrary());
}
// apply the new target if needed.
@@ -1113,11 +1031,34 @@ public final class Sdk {
}
};
- /** List of opened project. This is filled in {@link IProjectListener#projectOpened(IProject)}
- * and {@link IProjectListener#projectOpenedWithWorkspace(IProject)}, and processed in
+ /** List of modified projects. This is filled in
+ * {@link IProjectListener#projectOpened(IProject)},
+ * {@link IProjectListener#projectOpenedWithWorkspace(IProject)},
+ * {@link IProjectListener#projectClosed(IProject)}, and
+ * {@link IProjectListener#projectDeleted(IProject)} and processed in
* {@link IResourceEventListener#resourceChangeEventEnd()}.
*/
- private final ArrayList<ProjectState> mOpenedLibraryProjects = new ArrayList<ProjectState>();
+ private final List<ProjectState> mModifiedProjects = new ArrayList<ProjectState>();
+ private final List<ProjectState> mModifiedChildProjects = new ArrayList<ProjectState>();
+
+ private void markProject(ProjectState projectState, boolean updateParents) {
+ if (mModifiedProjects.contains(projectState) == false) {
+ if (DEBUG) {
+ System.out.println("\tMARKED: " + projectState.getProject().getName());
+ }
+ mModifiedProjects.add(projectState);
+ }
+
+ // if the project is resolved also add it to this list.
+ if (updateParents) {
+ if (mModifiedChildProjects.contains(projectState) == false) {
+ if (DEBUG) {
+ System.out.println("\tMARKED(child): " + projectState.getProject().getName());
+ }
+ mModifiedChildProjects.add(projectState);
+ }
+ }
+ }
/**
* Delegate listener for resource changes. This is called before and after any calls to the
@@ -1125,571 +1066,44 @@ public final class Sdk {
*/
private IResourceEventListener mResourceEventListener = new IResourceEventListener() {
public void resourceChangeEventStart() {
- // pass
+ mModifiedProjects.clear();
+ mModifiedChildProjects.clear();
}
public void resourceChangeEventEnd() {
- updateProjectsWithNewLibraries(mOpenedLibraryProjects);
- mOpenedLibraryProjects.clear();
- }
- };
-
- /**
- * Action bundle to update library links on a project.
- *
- * @see Sdk#queueLinkUpdateBundle(LinkUpdateBundle)
- * @see Sdk#updateLibraryLinks(LinkUpdateBundle, IProgressMonitor)
- */
- private static class LinkUpdateBundle {
-
- /** The main project receiving the library links. */
- IProject mProject = null;
- /** A list (possibly null/empty) of projects that should be linked. */
- IProject[] mNewLibraryProjects = null;
- /** an optional old library path that needs to be removed at the same time as the new
- * libraries are added. Can be <code>null</code> in which case no libraries are removed. */
- IPath mDeletedLibraryPath = null;
- /** A list (possibly null/empty) of projects that should be unlinked */
- IProject[] mRemovedLibraryProjects = null;
- /** Whether unknown IClasspathEntry (that were flagged as being added by ADT) are to be
- * removed. This is typically only set to <code>true</code> when the project is opened. */
- boolean mCleanupCPE = false;
-
- @Override
- public String toString() {
- return String.format(
- "LinkUpdateBundle: %1$s (clean: %2$s) > added: %3$s, removed: %4$s, deleted: %5$s", //$NON-NLS-1$
- mProject.getName(),
- mCleanupCPE,
- Arrays.toString(mNewLibraryProjects),
- Arrays.toString(mRemovedLibraryProjects),
- mDeletedLibraryPath);
- }
- }
- private final ArrayList<LinkUpdateBundle> mLinkActionBundleQueue =
- new ArrayList<LinkUpdateBundle>();
+ // first make sure all the parents are updated
+ updateParentProjects();
- /**
- * Queues a {@link LinkUpdateBundle} bundle to be run by a job.
- *
- * All action bundles are executed in a job in the exact order they are added.
- * This is convenient when several actions must be executed in a job consecutively (instead
- * of in parallel as it would happen if each started its own job) but it is impossible
- * to manually control the job that's running them (for instance each action is started from
- * different callbacks such as {@link IProjectListener#projectOpened(IProject)}.
- *
- * If the job is not yet started, or has terminated due to lack of action bundle, it is
- * restarted.
- *
- * @param bundle the action bundle to execute
- */
- private void queueLinkUpdateBundle(LinkUpdateBundle bundle) {
- boolean startJob = false;
- synchronized (mLinkActionBundleQueue) {
- startJob = mLinkActionBundleQueue.size() == 0;
- mLinkActionBundleQueue.add(bundle);
- }
+ // for all modified projects, update their library list
+ // and gather their IProject
+ final List<IJavaProject> projectList = new ArrayList<IJavaProject>();
+ for (ProjectState state : mModifiedProjects) {
+ state.updateFullLibraryList();
+ projectList.add(JavaCore.create(state.getProject()));
+ }
- if (startJob) {
Job job = new Job("Android Library Update") { //$NON-NLS-1$
@Override
protected IStatus run(IProgressMonitor monitor) {
- // loop until there's no bundle to process
- while (true) {
- // get the bundle, but don't remove until we're done, or a new job could be
- // started.
- LinkUpdateBundle bundle = null;
- synchronized (mLinkActionBundleQueue) {
- // there is always a bundle at this point, as they are only removed
- // at the end of this method, and the job is only started after adding
- // one
- bundle = mLinkActionBundleQueue.get(0);
- }
+ LibraryClasspathContainerInitializer.updateProjects(
+ projectList.toArray(new IJavaProject[projectList.size()]));
- // process the bundle.
+ for (IJavaProject javaProject : projectList) {
try {
- updateLibraryLinks(bundle, monitor);
- } catch (Exception e) {
- AdtPlugin.log(e, "Failed to process bundle: %1$s", //$NON-NLS-1$
- bundle.toString());
- }
-
- try {
- // force a recompile
- bundle.mProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor);
- } catch (Exception e) {
- // no need to log those.
- }
-
- // remove it from the list.
- synchronized (mLinkActionBundleQueue) {
- mLinkActionBundleQueue.remove(0);
-
- // no more bundle to process? done.
- if (mLinkActionBundleQueue.size() == 0) {
- return Status.OK_STATUS;
- }
+ javaProject.getProject().build(IncrementalProjectBuilder.FULL_BUILD,
+ monitor);
+ } catch (CoreException e) {
+ // pass
}
}
+ return Status.OK_STATUS;
}
};
job.setPriority(Job.BUILD);
job.schedule();
}
- }
-
-
- /**
- * Adds to a list the resolved {@link IProject} dependencies for a given {@link ProjectState}.
- * This recursively goes down to indirect dependencies.
- *
- * <strong>The list is filled in an order that is not valid for calling <code>aapt</code>
- * </strong>.
- * Use {@link ProjectState#getFullLibraryProjects()} for use with <code>aapt</code>.
- *
- * @param projectState the ProjectState of the project from which to add the libraries.
- * @param libraries the list of {@link IProject} to fill.
- */
- private void fillProjectDependenciesList(ProjectState projectState,
- ArrayList<IProject> libraries) {
- for (LibraryState libState : projectState.getLibraries()) {
- ProjectState libProjectState = libState.getProjectState();
-
- // only care if the LibraryState has a resolved ProjectState
- if (libProjectState != null) {
- // try not to add duplicate. This can happen if a project depends on 2 different
- // libraries that both depend on the same one.
- IProject libProject = libProjectState.getProject();
- if (libraries.contains(libProject) == false) {
- libraries.add(libProject);
- }
-
- // process the libraries of this library too.
- fillProjectDependenciesList(libProjectState, libraries);
- }
- }
- }
-
- /**
- * Sets up a path variable for a given project.
- * The name of the variable is based on the name of the project. However some valid character
- * for project names can be invalid for variable paths.
- * {@link #getLibraryVariableName(String)} return the name of the variable based on the
- * project name.
- *
- * @param libProject the project
- *
- * @see IPathVariableManager
- * @see #getLibraryVariableName(String)
- */
- 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 varName = getLibraryVariableName(libProject.getName());
-
- if (libPath.equals(pathVarMgr.getValue(varName)) == false) {
- try {
- pathVarMgr.setValue(varName, libPath);
- } catch (CoreException e) {
- AdtPlugin.logAndPrintError(e, "Library Project",
- "Unable to set linked path var '%1$s' for library %2$s: %3$s", //$NON-NLS-1$
- varName, libPath.toOSString(), e.getMessage());
- }
- }
- }
-
-
- /**
- * Deletes the path variable that was setup for the given project.
- * @param project the project
- * @see #disposeLibraryProject(String)
- */
- private void disposeLibraryProject(IProject project) {
- disposeLibraryProject(project.getName());
- }
-
- /**
- * Deletes the path variable that was setup for the given project name.
- * The name of the variable is based on the name of the project. However some valid character
- * for project names can be invalid for variable paths.
- * {@link #getLibraryVariableName(String)} return the name of the variable based on the
- * project name.
- * @param projectName the name of the project, unmodified.
- */
- private void disposeLibraryProject(String projectName) {
- IPathVariableManager pathVarMgr =
- ResourcesPlugin.getWorkspace().getPathVariableManager();
-
- final String varName = getLibraryVariableName(projectName);
-
- // remove the value by setting the value to null.
- try {
- pathVarMgr.setValue(varName, null /*path*/);
- } catch (CoreException e) {
- String message = String.format("Unable to remove linked path var '%1$s'", //$NON-NLS-1$
- varName);
- AdtPlugin.log(e, message);
- }
- }
-
- /**
- * Returns a valid path variable name based on the name of a library project.
- * @param name the name of the library project.
- */
- private String getLibraryVariableName(String name) {
- /*
- * From the javadoc of IPathVariableManager:
- * A path variable is a pair of non-null elements (name,value) where name is a
- * case-sensitive string (containing only letters, digits and the underscore character,
- * and not starting with a digit), and value is an absolute IPath object.
- */
-
- // the variable name is made by:
- // - prepending _android_ (this ensure there's no digit at the start)
- // - removing all unsupported characters.
- // - append the hashcode of the original name. This should help reduce collisions.
- String validName = name.replaceAll("[^0-9a-zA-Z]+", "_"); //$NON-NLS-1$ //$NON-NLS-2$
-
- //ensure the valid is not negative as - is not a valid char
- long hash = name.hashCode() & 0x00000000ffffffffL;
- return "_android_" + validName + "_" + Long.toString(hash, 16) ; //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- /**
- * Update the library links for a project
- *
- * This does the follow:
- * - add/remove the library projects to the main projects dynamic reference list. This is used
- * by the builders to receive resource change deltas for library projects and figure out what
- * needs to be recompiled/recreated.
- * - create new {@link IClasspathEntry} of type {@link IClasspathEntry#CPE_SOURCE} for each
- * source folder for each new library project.
- * - remove the {@link IClasspathEntry} of type {@link IClasspathEntry#CPE_SOURCE} for each
- * source folder for each removed library project.
- * - If {@link LinkUpdateBundle#mCleanupCPE} is set to true, all CPE created by ADT that cannot
- * be resolved are removed. This should only be used when the project is opened.
- *
- * <strong>This must not be called directly. Instead the {@link LinkUpdateBundle} must
- * be run through a job with {@link #queueLinkUpdateBundle(LinkUpdateBundle)}.</strong>
- *
- * @param bundle The {@link LinkUpdateBundle} action bundle that contains all the parameters
- * necessary to execute the action.
- * @param monitor an {@link IProgressMonitor}.
- * @return an {@link IStatus} with the status of the action.
- */
- private IStatus updateLibraryLinks(LinkUpdateBundle bundle, IProgressMonitor monitor) {
- if (bundle.mProject.isOpen() == false) {
- return Status.OK_STATUS;
- }
- try {
- // add the library to the list of dynamic references. This is necessary to receive
- // notifications that the library content changed in the builders.
- IProjectDescription projectDescription = bundle.mProject.getDescription();
- IProject[] refs = projectDescription.getDynamicReferences();
-
- if (refs.length > 0) {
- ArrayList<IProject> list = new ArrayList<IProject>(Arrays.asList(refs));
-
- // remove a previous library if needed (in case of a rename)
- if (bundle.mDeletedLibraryPath != null) {
- // since project basically have only one segment that matter,
- // just check the names
- removeFromList(list, bundle.mDeletedLibraryPath.lastSegment());
- }
-
- if (bundle.mRemovedLibraryProjects != null) {
- for (IProject removedProject : bundle.mRemovedLibraryProjects) {
- removeFromList(list, removedProject.getName());
- }
- }
-
- // add the new ones if they don't exist
- if (bundle.mNewLibraryProjects != null) {
- for (IProject newProject : bundle.mNewLibraryProjects) {
- if (list.contains(newProject) == false) {
- list.add(newProject);
- }
- }
- }
-
- // set the changed list
- projectDescription.setDynamicReferences(
- list.toArray(new IProject[list.size()]));
- } else {
- if (bundle.mNewLibraryProjects != null) {
- projectDescription.setDynamicReferences(bundle.mNewLibraryProjects);
- }
- }
-
- // get the current classpath entries for the project to add the new source
- // folders.
- IJavaProject javaProject = JavaCore.create(bundle.mProject);
- IClasspathEntry[] entries = javaProject.getRawClasspath();
- ArrayList<IClasspathEntry> classpathEntries = new ArrayList<IClasspathEntry>(
- Arrays.asList(entries));
-
- IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
-
- // loop on the classpath entries and look for CPE_SOURCE entries that
- // are linked folders, then record them for comparison later as we add the new
- // ones.
- ArrayList<IClasspathEntry> cpeToRemove = new ArrayList<IClasspathEntry>();
- for (IClasspathEntry classpathEntry : classpathEntries) {
- if (classpathEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
- IPath path = classpathEntry.getPath();
- IResource linkedRes = wsRoot.findMember(path);
- if (linkedRes != null && linkedRes.isLinked() &&
- CREATOR_ADT.equals(ProjectHelper.loadStringProperty(
- linkedRes, PROP_CREATOR))) {
-
- // add always to list if we're doing clean-up
- if (bundle.mCleanupCPE) {
- cpeToRemove.add(classpathEntry);
- } else {
- String libName = ProjectHelper.loadStringProperty(linkedRes,
- PROP_LIBRARY_NAME);
- if (libName != null && isRemovedLibrary(bundle, libName)) {
- cpeToRemove.add(classpathEntry);
- }
- }
- }
- }
- }
-
- // loop on the projects to add.
- if (bundle.mNewLibraryProjects != null) {
- for (IProject library : bundle.mNewLibraryProjects) {
- if (library.isOpen() == false) {
- continue;
- }
- final String libName = library.getName();
- final String varName = getLibraryVariableName(libName);
-
- // get the list of source folders for the library.
- List<IPath> sourceFolderPaths = BaseProjectHelper.getSourceClasspaths(library);
-
- // loop on all the source folder, ignoring FD_GEN and add them
- // as linked folder
- for (IPath sourceFolderPath : sourceFolderPaths) {
- IResource sourceFolder = wsRoot.findMember(sourceFolderPath);
- if (sourceFolder == null || sourceFolder.isLinked()) {
- continue;
- }
-
- IPath relativePath = sourceFolder.getProjectRelativePath();
- if (SdkConstants.FD_GEN_SOURCES.equals(relativePath.toString())) {
- continue;
- }
-
- // create the linked path
- IPath linkedPath = new Path(varName).append(relativePath);
-
- // look for an existing CPE that has the same linked path and that was
- // going to be removed.
- IClasspathEntry match = findClasspathEntryMatch(cpeToRemove, linkedPath,
- null);
-
- if (match == null) {
- // no match, create one
- // get a string version, to make up the linked folder name
- String srcFolderName = relativePath.toString().replace(
- "/", //$NON-NLS-1$
- "_"); //$NON-NLS-1$
-
- // folder name
- String folderName = libName + "_" + srcFolderName; //$NON-NLS-1$
-
- // create a linked resource for the library using the path var.
- IFolder libSrc = bundle.mProject.getFolder(folderName);
- IPath libSrcPath = libSrc.getFullPath();
-
- // check if there's a CPE that would conflict, in which case it needs to
- // be removed (this can happen for existing CPE that don't match an open
- // project)
- match = findClasspathEntryMatch(classpathEntries, null/*rawPath*/,
- libSrcPath);
- if (match != null) {
- classpathEntries.remove(match);
- }
-
- // the path of the linked resource is based on the path variable
- // representing the library project, followed by the source folder name.
- libSrc.createLink(linkedPath, IResource.REPLACE, monitor);
-
- // set some persistent properties on it to know that it was
- // created by ADT.
- ProjectHelper.saveStringProperty(libSrc, PROP_CREATOR, CREATOR_ADT);
- ProjectHelper.saveResourceProperty(libSrc, PROP_LIBRARY, library);
- ProjectHelper.saveStringProperty(libSrc, PROP_LIBRARY_NAME,
- library.getName());
-
- // add the source folder to the classpath entries
- classpathEntries.add(JavaCore.newSourceEntry(libSrcPath));
- } else {
- // there's a valid match, do nothing, but remove the match from
- // the list of previously existing CPE.
- cpeToRemove.remove(match);
- }
- }
- }
- }
-
- // remove the CPE that should be removed.
- classpathEntries.removeAll(cpeToRemove);
-
- // set the new list
- javaProject.setRawClasspath(
- classpathEntries.toArray(new IClasspathEntry[classpathEntries.size()]),
- monitor);
-
- // and delete the folders of the CPE that were removed (must be done after)
- for (IClasspathEntry cpe : cpeToRemove) {
- IResource res = wsRoot.findMember(cpe.getPath());
- res.delete(true, monitor);
- }
-
- return Status.OK_STATUS;
- } catch (CoreException e) {
- AdtPlugin.logAndPrintError(e, bundle.mProject.getName(),
- "Failed to create library links: %1$s", //$NON-NLS-1$
- e.getMessage());
- return e.getStatus();
- }
- }
-
- private boolean isRemovedLibrary(LinkUpdateBundle bundle, String libName) {
- if (bundle.mDeletedLibraryPath != null &&
- libName.equals(bundle.mDeletedLibraryPath.lastSegment())) {
- return true;
- }
-
- if (bundle.mRemovedLibraryProjects != null) {
- for (IProject removedProject : bundle.mRemovedLibraryProjects) {
- if (libName.equals(removedProject.getName())) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- * Computes the library difference based on a previous list and a current state, and creates
- * a {@link LinkUpdateBundle} action to update the given project.
- * @param project The current project state
- * @param oldLibraries the list of old libraries. Typically the result of
- * {@link ProjectState#getFullLibraryProjects()} before the ProjectState is updated.
- * @return null if there no action to take, or a {@link LinkUpdateBundle} object to run.
- */
- private LinkUpdateBundle getLinkBundle(ProjectState project, List<IProject> oldLibraries) {
- // get the new full list of projects
- List<IProject> newLibraries = project.getFullLibraryProjects();
-
- // and build the real difference. A list of new projects and a list of
- // removed project.
- // This is not the same as the added/removed libraries because libraries
- // could be indirect dependencies through several different direct
- // dependencies so it's easier to compare the full lists before and after
- // the reload.
-
- List<IProject> addedLibs = new ArrayList<IProject>();
- List<IProject> removedLibs = new ArrayList<IProject>();
-
- // first get the list of new projects.
- for (IProject newLibrary : newLibraries) {
- boolean found = false;
- for (IProject oldLibrary : oldLibraries) {
- if (newLibrary.equals(oldLibrary)) {
- found = true;
- break;
- }
- }
-
- // if it was not found in the old libraries, it's really new
- if (found == false) {
- addedLibs.add(newLibrary);
- }
- }
-
- // now the list of removed projects.
- for (IProject oldLibrary : oldLibraries) {
- boolean found = false;
- for (IProject newLibrary : newLibraries) {
- if (newLibrary.equals(oldLibrary)) {
- found = true;
- break;
- }
- }
-
- // if it was not found in the new libraries, it's really been removed
- if (found == false) {
- removedLibs.add(oldLibrary);
- }
- }
-
- if (addedLibs.size() > 0 || removedLibs.size() > 0) {
- LinkUpdateBundle bundle = new LinkUpdateBundle();
- bundle.mProject = project.getProject();
- bundle.mNewLibraryProjects =
- addedLibs.toArray(new IProject[addedLibs.size()]);
- bundle.mRemovedLibraryProjects =
- removedLibs.toArray(new IProject[removedLibs.size()]);
- return bundle;
- }
-
- return null;
- }
-
- /**
- * Removes a project from a list based on its name.
- * @param projects the list of projects.
- * @param name the name of the project to remove.
- */
- private void removeFromList(List<IProject> projects, String name) {
- final int count = projects.size();
- for (int i = 0 ; i < count ; i++) {
- // since project basically have only one segment that matter,
- // just check the names
- if (projects.get(i).getName().equals(name)) {
- projects.remove(i);
- return;
- }
- }
- }
-
- /**
- * Returns a {@link IClasspathEntry} from the given list whose linked path match the given path.
- * @param cpeList a list of {@link IClasspathEntry} of {@link IClasspathEntry#getEntryKind()}
- * {@link IClasspathEntry#CPE_SOURCE} whose {@link IClasspathEntry#getPath()}
- * points to a linked folder.
- * @param rawPath the raw path to compare to. Can be null if <var>path</var> is used instead.
- * @param path the path to compare to. Can be null if <var>rawPath</var> is used instead.
- * @return the matching IClasspathEntry or null.
- */
- private IClasspathEntry findClasspathEntryMatch(ArrayList<IClasspathEntry> cpeList,
- IPath rawPath, IPath path) {
- IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
- for (IClasspathEntry cpe : cpeList) {
- IPath cpePath = cpe.getPath();
- // test the normal path of the resource.
- if (path != null && path.equals(cpePath)) {
- return cpe;
- }
-
- IResource res = wsRoot.findMember(cpePath);
- // getRawLocation returns the path that the linked folder points to.
- if (rawPath != null && res.getRawLocation().equals(rawPath)) {
- return cpe;
- }
-
- }
- return null;
- }
+ };
/**
* Updates all existing projects with a given list of new/updated libraries.
@@ -1697,65 +1111,33 @@ public final class Sdk {
* library project, and if they do, they are linked together.
* @param libraries the list of new/updated library projects.
*/
- private void updateProjectsWithNewLibraries(List<ProjectState> libraries) {
- if (libraries.size() == 0) {
+ private void updateParentProjects() {
+ if (mModifiedChildProjects.size() == 0) {
return;
}
- ArrayList<ProjectState> updatedLibraries = new ArrayList<ProjectState>();
- synchronized (sLock) {
- // for each projects, look for projects that depend on it, and update them.
- // Once they are updated (meaning ProjectState#needs() has been called on them),
- // we add them to the list so that can be updated as well.
- for (ProjectState projectState : sProjectStateMap.values()) {
- // record the current library dependencies
- List<IProject> oldLibraries = projectState.getFullLibraryProjects();
-
- boolean needLibraryDependenciesUpdated = false;
- for (ProjectState library : libraries) {
- // Normally we would only need to test if ProjectState#needs returns non null,
- // meaning the link between the project and the library has not been
- // done yet.
- // However what matters here is that the library is a dependency,
- // period. If the library project was updated, then we redo the link,
- // with all indirect dependencies (which *have* changed, since this is
- // what this method is all about.)
- // We still need to call ProjectState#needs to make the link in case it's not
- // been done yet (which can happen if the library project was just opened).
- if (projectState != library) {
- // call needs in case this new library was just opened, and the link needs
- // to be done
- LibraryState libState = projectState.needs(library);
- if (libState == null && projectState.dependsOn(library)) {
- // ProjectState.needs only returns true if the library was needed.
- // but we also need to check the case where the project depends on
- // the library but the link was already done.
- needLibraryDependenciesUpdated = true;
- }
- }
+ ArrayList<ProjectState> childProjects = new ArrayList<ProjectState>(mModifiedChildProjects);
+ mModifiedChildProjects.clear();
+ synchronized (LOCK) {
+ // for each project for which we must update its parent, we loop on the parent
+ // projects and adds them to the list of modified projects. If they are themselves
+ // libraries, we add them too.
+ for (ProjectState state : childProjects) {
+ if (DEBUG) {
+ System.out.println(">>> Updating parents of " + state.getProject().getName());
}
-
- if (needLibraryDependenciesUpdated) {
- projectState.updateFullLibraryList();
+ List<ProjectState> parents = state.getParentProjects();
+ for (ProjectState parent : parents) {
+ markProject(parent, parent.isLibrary());
}
-
- LinkUpdateBundle bundle = getLinkBundle(projectState, oldLibraries);
- if (bundle != null) {
- queueLinkUpdateBundle(bundle);
-
- // if this updated project is a library, add it to the list, so that
- // projects depending on it get updated too.
- if (projectState.isLibrary() &&
- updatedLibraries.contains(projectState) == false) {
- updatedLibraries.add(projectState);
- }
+ if (DEBUG) {
+ System.out.println("<<<");
}
}
}
- // done, but there may be updated projects that were libraries, so we need to do the same
- // for this libraries, to update the project there were depending on.
- updateProjectsWithNewLibraries(updatedLibraries);
+ // done, but there may be parents that are also libraries. Need to update their parents.
+ updateParentProjects();
}
}
diff --git a/files/ant/lib_rules.xml b/files/ant/lib_rules.xml
deleted file mode 100644
index 42a1e48..0000000
--- a/files/ant/lib_rules.xml
+++ /dev/null
@@ -1,186 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project name="android_rules" default="debug">
-
- <!--
- This rules file is meant to be imported by the custom Ant task:
- com.android.ant.SetupTask
-
- The following properties are put in place by the importing task:
- android.jar, android.aidl, aapt, aidl, and dx
-
- Additionnaly, the task sets up the following classpath reference:
- android.target.classpath
- This is used by the compiler task as the boot classpath.
- -->
-
- <!-- Custom tasks -->
- <taskdef name="aapt"
- classname="com.android.ant.AaptExecLoopTask"
- classpathref="android.antlibs" />
-
- <taskdef name="aidl"
- classname="com.android.ant.AidlExecTask"
- classpathref="android.antlibs" />
-
- <taskdef name="xpath"
- classname="com.android.ant.XPathTask"
- classpathref="android.antlibs" />
-
- <taskdef name="if"
- classname="com.android.ant.IfElseTask"
- classpathref="android.antlibs" />
-
- <!-- Properties -->
-
- <property name="android.tools.dir" location="${sdk.dir}/tools" />
- <!-- Name of the application package extracted from manifest file -->
- <xpath input="AndroidManifest.xml" expression="/manifest/@package"
- output="manifest.package" />
- <xpath input="AndroidManifest.xml" expression="/manifest/application/@android:hasCode"
- output="manifest.hasCode" default="true"/>
-
- <!-- Input directories -->
- <property name="source.dir" value="src" />
- <property name="source.absolute.dir" location="${source.dir}" />
- <property name="gen.dir" value="gen" />
- <property name="gen.absolute.dir" location="${gen.dir}" />
- <property name="resource.dir" value="res" />
- <property name="resource.absolute.dir" location="${resource.dir}" />
- <property name="asset.dir" value="assets" />
- <property name="asset.absolute.dir" location="${asset.dir}" />
-
- <!-- Directory for the third party java libraries -->
- <property name="external.libs.dir" value="libs" />
- <property name="external.libs.absolute.dir" location="${external.libs.dir}" />
- <!-- Directory for the native libraries -->
- <property name="native.libs.dir" value="libs" />
- <property name="native.libs.absolute.dir" location="${native.libs.dir}" />
-
- <!-- Output directories -->
- <property name="out.dir" value="bin" />
- <property name="out.absolute.dir" location="${out.dir}" />
- <property name="out.classes.dir" value="${out.absolute.dir}/classes" />
- <property name="out.classes.absolute.dir" location="${out.classes.dir}" />
-
- <!-- compilation options -->
- <property name="java.encoding" value="UTF-8" />
- <property name="java.target" value="1.5" />
- <property name="java.source" value="1.5" />
-
- <!-- Verbosity -->
- <property name="verbose" value="false" />
- <!-- This is needed by emma as it uses multilevel verbosity instead of simple 'true' or 'false'
- The property 'verbosity' is not user configurable and depends exclusively on 'verbose'
- value.-->
- <condition property="verbosity" value="verbose" else="quiet">
- <istrue value="${verbose}" />
- </condition>
-
- <!-- Tools -->
- <condition property="exe" value=".exe" else=""><os family="windows" /></condition>
-
- <!-- Emma configuration -->
- <property name="emma.dir" value="${sdk.dir}/tools/lib" />
- <path id="emma.lib">
- <pathelement location="${emma.dir}/emma.jar" />
- <pathelement location="${emma.dir}/emma_ant.jar" />
- </path>
- <taskdef resource="emma_ant.properties" classpathref="emma.lib" />
- <!-- End of emma configuration -->
-
- <!-- Rules -->
-
- <!-- Creates the output directories if they don't exist yet. -->
- <target name="-dirs">
- <echo>Creating output directories if needed...</echo>
- <mkdir dir="${resource.absolute.dir}" />
- <mkdir dir="${external.libs.absolute.dir}" />
- <mkdir dir="${gen.absolute.dir}" />
- <mkdir dir="${out.absolute.dir}" />
- <mkdir dir="${out.classes.absolute.dir}" />
- </target>
-
- <!-- empty default pre-build target. Create a similar target in
- your build.xml and it'll be called instead of this one. -->
- <target name="-pre-build"/>
-
- <!-- Generates the R.java file for this project's resources. -->
- <target name="-resource-src" depends="-dirs, -pre-build">
- <echo>Generating R.java / Manifest.java from the resources...</echo>
- <aapt executable="${aapt}"
- command="package"
- verbose="${verbose}"
- manifest="AndroidManifest.xml"
- androidjar="${android.jar}"
- rfolder="${gen.absolute.dir}">
- <res path="${resource.absolute.dir}" />
- </aapt>
- </target>
-
- <!-- Generates java classes from .aidl files. -->
- <target name="-aidl" depends="-dirs">
- <if condition="${manifest.hasCode}">
- <then>
- <echo>Compiling aidl files into Java classes...</echo>
- <aidl executable="${aidl}" framework="${android.aidl}"
- genFolder="${gen.absolute.dir}">
- <source path="${source.absolute.dir}"/>
- <source refid="android.libraries.src"/>
- </aidl>
- </then>
- <else>
- <echo>hasCode = false. Skipping...</echo>
- </else>
- </if>
- </target>
-
- <!-- empty default pre-compile target. Create a similar target in
- your build.xml and it'll be called instead of this one. -->
- <target name="-pre-compile"/>
-
- <!-- Compiles this project's .java files into .class files. -->
- <target name="compile" depends="-resource-src, -aidl, -pre-compile"
- description="Compiles project's .java files into .class files">
- <!-- If android rules are used for a test project, its classpath should include
- tested project's location -->
- <condition property="extensible.classpath"
- value="${tested.project.absolute.dir}/bin/classes" else=".">
- <isset property="tested.project.absolute.dir" />
- </condition>
- <condition property="extensible.libs.classpath"
- value="${tested.project.absolute.dir}/libs"
- else="./libs">
- <isset property="tested.project.absolute.dir" />
- </condition>
- <javac encoding="${java.encoding}"
- source="${java.source}" target="${java.target}"
- debug="true" extdirs=""
- destdir="${out.classes.absolute.dir}"
- bootclasspathref="android.target.classpath"
- verbose="${verbose}"
- classpath="${extensible.classpath}"
- classpathref="android.libraries.jars">
- <src path="${source.absolute.dir}" />
- <src path="${gen.absolute.dir}" />
- <src refid="android.libraries.src" />
- <classpath>
- <fileset dir="${external.libs.absolute.dir}" includes="*.jar" />
- <fileset dir="${extensible.libs.classpath}" includes="*.jar" />
- </classpath>
- </javac>
- </target>
-
- <target name="clean" description="Removes output files created by other targets.">
- <delete dir="${out.absolute.dir}" verbose="${verbose}" />
- <delete dir="${gen.absolute.dir}" verbose="${verbose}" />
- </target>
-
- <target name="help">
- <!-- displays starts at col 13
- |13 80| -->
- <echo>Android Ant Build. Available targets:</echo>
- <echo> help: Displays this help.</echo>
- <echo> clean: Removes output files created by other targets.</echo>
- <echo> compile: Compiles project's .java files into .class files.</echo>
- </target>
-</project>
diff --git a/files/ant/main_rules.xml b/files/ant/main_rules.xml
index 8404eb8..ca4cfc9 100644
--- a/files/ant/main_rules.xml
+++ b/files/ant/main_rules.xml
@@ -1,136 +1,146 @@
<?xml version="1.0" encoding="UTF-8"?>
-<project name="android_rules" default="debug">
+<project name="" default="debug">
<!--
- This rules file is meant to be imported by the custom Ant task:
- com.android.ant.SetupTask
+ This build file is imported by the project build file. It contains
+ all the targets and tasks necessary to build Android projects, be they
+ regular projects, library projects, or test projects.
+
+ At the beginning of the file is a list of properties that can be overridden
+ by adding them to your build.properties (properties are immutable, so their
+ first definition sticks and is never changed).
+
+ Follows:
+ - custom task definitions,
+ - more properties (do not override those unless the whole build system is modified).
+ - macros used throughout the build,
+ - base build targets,
+ - debug-specific build targets,
+ - release-specific build targets,
+ - instrument-specific build targets,
+ - test project-specific build targets,
+ - install targets,
+ - help target
+ -->
- The following properties are put in place by the importing task:
- android.jar, android.aidl, aapt, aidl, and dx
+ <!-- ********** Overrideable Properties ********** -->
- Additionnaly, the task sets up the following classpath reference:
- android.target.classpath
- This is used by the compiler task as the boot classpath.
- -->
+ <!-- You can override these values in your build.xml or build.properties.
+ Overriding any other properties may result in broken build. -->
+
+ <!-- Tells adb which device to target. You can change this from the command line
+ by invoking "ant -Dadb.device.arg=-d" for device "ant -Dadb.device.arg=-e" for
+ the emulator. -->
+ <property name="adb.device.arg" value="" />
+
+ <!-- fileset exclude patterns (space separated) to prevent
+ files inside src/ from being packaged. -->
+ <property name="android.package.excludes" value="" />
+
+ <!-- set some properties used for filtering/override. If those weren't defined
+ before, then this will create them with empty values, which are then ignored
+ by the custom tasks receiving them. -->
+ <property name="version.code" value="" />
+ <property name="version.name" value="" />
+ <property name="aapt.resource.filter" value="" />
+ <property name="filter.abi" value="" />
+
+ <!-- compilation options -->
+ <property name="java.encoding" value="UTF-8" />
+ <property name="java.target" value="1.5" />
+ <property name="java.source" value="1.5" />
+
+ <!-- Verbosity -->
+ <property name="verbose" value="false" />
+
+ <!-- ********** Custom Tasks ********** -->
+
+ <!-- jar file from where the tasks are loaded -->
+ <path id="android.antlibs">
+ <pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
+ </path>
<!-- Custom tasks -->
+ <taskdef name="setup"
+ classname="com.android.ant.NewSetupTask"
+ classpathref="android.antlibs" />
+
<taskdef name="aapt"
- classname="com.android.ant.AaptExecLoopTask"
- classpathref="android.antlibs" />
+ classname="com.android.ant.AaptExecLoopTask"
+ classpathref="android.antlibs" />
<taskdef name="aidl"
- classname="com.android.ant.AidlExecTask"
- classpathref="android.antlibs" />
+ classname="com.android.ant.AidlExecTask"
+ classpathref="android.antlibs" />
<taskdef name="renderscript"
- classname="com.android.ant.RenderScriptTask"
- classpathref="android.antlibs" />
+ classname="com.android.ant.RenderScriptTask"
+ classpathref="android.antlibs" />
<taskdef name="apkbuilder"
- classname="com.android.ant.ApkBuilderTask"
- classpathref="android.antlibs" />
+ classname="com.android.ant.ApkBuilderTask"
+ classpathref="android.antlibs" />
<taskdef name="xpath"
- classname="com.android.ant.XPathTask"
- classpathref="android.antlibs" />
+ classname="com.android.ant.XPathTask"
+ classpathref="android.antlibs" />
<taskdef name="if"
- classname="com.android.ant.IfElseTask"
- classpathref="android.antlibs" />
+ classname="com.android.ant.IfElseTask"
+ classpathref="android.antlibs" />
- <!-- Properties -->
+ <!-- Emma configuration -->
+ <property name="emma.dir" value="${sdk.dir}/tools/lib" />
+ <path id="emma.lib">
+ <pathelement location="${emma.dir}/emma.jar" />
+ <pathelement location="${emma.dir}/emma_ant.jar" />
+ </path>
+ <taskdef resource="emma_ant.properties" classpathref="emma.lib" />
+ <!-- End of emma configuration -->
- <!-- Tells adb which device to target. You can change this from the command line
- by invoking "ant -Dadb.device.arg=-d" for device "ant -Dadb.device.arg=-e" for
- the emulator. -->
- <property name="adb.device.arg" value="" />
- <property name="android.tools.dir" location="${sdk.dir}/tools" />
- <property name="android.platform.tools.dir" location="${sdk.dir}/platform-tools" />
- <!-- Name of the application package extracted from manifest file -->
- <xpath input="AndroidManifest.xml" expression="/manifest/@package"
- output="manifest.package" />
- <!-- Value of the hasCode attribute (Application node) extracted from manifest file -->
- <xpath input="AndroidManifest.xml" expression="/manifest/application/@android:hasCode"
- output="manifest.hasCode" default="true"/>
+ <!-- ********** Other Properties ********** -->
+ <!-- overriding these properties may break the build
+ unless the whole file is updated -->
<!-- Input directories -->
<property name="source.dir" value="src" />
<property name="source.absolute.dir" location="${source.dir}" />
- <property name="gen.dir" value="gen" />
- <property name="gen.absolute.dir" location="${gen.dir}" />
- <property name="resource.dir" value="res" />
- <property name="resource.absolute.dir" location="${resource.dir}" />
- <property name="asset.dir" value="assets" />
- <property name="asset.absolute.dir" location="${asset.dir}" />
-
- <!-- Directory for the third party java libraries -->
+ <property name="gen.absolute.dir" location="gen" />
+ <property name="resource.absolute.dir" location="res" />
+ <property name="asset.absolute.dir" location="assets" />
<property name="jar.libs.dir" value="libs" />
<property name="jar.libs.absolute.dir" location="${jar.libs.dir}" />
- <!-- create a path with all the jar files, from the main project and the
- libraries -->
- <path id="jar.libs.ref">
- <fileset dir="${jar.libs.absolute.dir}" includes="*.jar" />
- <path refid="project.libraries.jars" />
- </path>
-
- <!-- Directory for the native libraries -->
- <property name="native.libs.dir" value="libs" />
- <property name="native.libs.absolute.dir" location="${native.libs.dir}" />
+ <property name="native.libs.absolute.dir" location="libs" />
<!-- Output directories -->
<property name="out.dir" value="bin" />
<property name="out.absolute.dir" location="${out.dir}" />
- <property name="out.classes.dir" value="${out.absolute.dir}/classes" />
- <property name="out.classes.absolute.dir" location="${out.classes.dir}" />
- <property name="out.resource.dir" location="${out.dir}/res" />
- <property name="out.resource.absolute.dir" location="${out.resource.dir}" />
-
+ <property name="out.classes.absolute.dir" location="${out.dir}/classes" />
+ <property name="out.res.absolute.dir" location="${out.dir}/res" />
+
+ <!-- tools location -->
+ <property name="android.tools.dir" location="${sdk.dir}/tools" />
+ <property name="android.platform.tools.dir" location="${sdk.dir}/platform-tools" />
+ <condition property="exe" value=".exe" else=""><os family="windows" /></condition>
+ <condition property="bat" value=".bat" else=""><os family="windows" /></condition>
+ <property name="adb" location="${android.platform.tools.dir}/adb${exe}" />
+ <property name="zipalign" location="${android.tools.dir}/zipalign${exe}" />
+ <property name="aidl" location="${android.platform.tools.dir}/aidl${exe}" />
+ <property name="aapt" location="${android.platform.tools.dir}/aapt${exe}" />
+ <property name="dx" location="${android.platform.tools.dir}/dx${bat}" />
+ <!-- renderscript location is set by NewSetupTask since we have a choice of
+ several executables based on minSdkVersion -->
+
<!-- Intermediate files -->
<property name="dex.file.name" value="classes.dex" />
- <property name="intermediate.dex.file"
- location="${out.absolute.dir}/${dex.file.name}" />
- <property name="resource.package.file.name"
- value="${ant.project.name}.ap_" />
-
- <!-- The final package file to generate
- These can be overridden by setting them earlier to
- different values -->
- <property name="out.debug.unaligned.file"
- location="${out.absolute.dir}/${ant.project.name}-debug-unaligned.apk" />
- <property name="out.debug.file"
- location="${out.absolute.dir}/${ant.project.name}-debug.apk" />
-
- <property name="out.unsigned.file.name"
- value="${ant.project.name}-unsigned.apk" />
- <property name="out.unsigned.file"
- location="${out.absolute.dir}/${out.unsigned.file.name}" />
-
- <property name="out.unaligned.file.name"
- value="${ant.project.name}-unaligned.apk" />
- <property name="out.unaligned.file"
- location="${out.absolute.dir}/${out.unaligned.file.name}" />
-
- <property name="out.release.file.name"
- value="${ant.project.name}-release.apk" />
- <property name="out.release.file"
- location="${out.absolute.dir}/${out.release.file.name}" />
+ <property name="intermediate.dex.file" location="${out.absolute.dir}/${dex.file.name}" />
+ <property name="resource.package.file.name" value="${ant.project.name}.ap_" />
- <!-- set some properties used for filtering/override. If those weren't defined
- before, then this will create them with empty values, which are then ignored
- by the custom tasks receiving them. -->
- <property name="version.code" value="" />
- <property name="version.name" value="" />
- <property name="aapt.resource.filter" value="" />
- <property name="filter.abi" value="" />
+ <!-- Build property file -->
+ <property name="out.build.prop.file" location="${out.absolute.dir}/build.prop" />
- <!-- compilation options -->
- <property name="java.encoding" value="UTF-8" />
- <property name="java.target" value="1.5" />
- <property name="java.source" value="1.5" />
- <!-- Verbosity -->
- <property name="verbose" value="false" />
<!-- This is needed by emma as it uses multilevel verbosity instead of simple 'true' or 'false'
The property 'verbosity' is not user configurable and depends exclusively on 'verbose'
value.-->
@@ -166,21 +176,54 @@
<!-- properties for packaging -->
<property name="build.packaging.nocrunch" value="true" />
- <!-- Tools -->
- <condition property="exe" value=".exe" else=""><os family="windows" /></condition>
- <property name="adb" location="${android.platform.tools.dir}/adb${exe}" />
- <property name="zipalign" location="${android.tools.dir}/zipalign${exe}" />
+ <!-- Name of the application package extracted from manifest file -->
+ <xpath input="AndroidManifest.xml" expression="/manifest/@package"
+ output="manifest.package" />
- <!-- Emma configuration -->
- <property name="emma.dir" value="${sdk.dir}/tools/lib" />
- <path id="emma.lib">
- <pathelement location="${emma.dir}/emma.jar" />
- <pathelement location="${emma.dir}/emma_ant.jar" />
- </path>
- <taskdef resource="emma_ant.properties" classpathref="emma.lib" />
- <!-- End of emma configuration -->
- <!-- Macros -->
+ <!-- ********** Macros ********** -->
+
+ <!-- macro to do a task on if project.is.library is false.
+ elseText attribute is displayed otherwise -->
+ <macrodef name="do-only-if-not-library">
+ <attribute name="elseText" />
+ <element name="task-to-do" implicit="yes" />
+ <sequential>
+ <if condition="${project.is.library}">
+ <else>
+ <task-to-do />
+ </else>
+ <then>
+ <echo>@{elseText}</echo>
+ </then>
+ </if>
+ </sequential>
+ </macrodef>
+
+ <!-- macro to do a task on if manifest.hasCode is true.
+ elseText attribute is displayed otherwise -->
+ <macrodef name="do-only-if-manifest-hasCode">
+ <attribute name="elseText" default=""/>
+ <element name="task-to-do" implicit="yes" />
+ <sequential>
+ <if condition="${manifest.hasCode}">
+ <then>
+ <task-to-do />
+ </then>
+ <else>
+ <if>
+ <condition>
+ <length string="@{elseText}" trim="true" when="greater" length="0" />
+ </condition>
+ <then>
+ <echo>@{elseText}</echo>
+ </then>
+ </if>
+ </else>
+ </if>
+ </sequential>
+ </macrodef>
+
<!-- Configurable macro, which allows to pass as parameters output directory,
output dex filename and external libraries to dex (optional) -->
@@ -221,7 +264,7 @@
<!-- This is macro that enable passing variable list of external jar files to ApkBuilder
Example of use:
- <package-helper output.filepath="/path/to/foo.apk">
+ <package-helper>
<extra-jars>
<jarfolder path="my_jars" />
<jarfile path="foo/bar.jar" />
@@ -229,23 +272,20 @@
</extra-jars>
</package-helper> -->
<macrodef name="package-helper">
- <attribute name="output.filepath" />
<element name="extra-jars" optional="yes" />
<sequential>
<apkbuilder
outfolder="${out.absolute.dir}"
resourcefile="${resource.package.file.name}"
- apkfilepath="@{output.filepath}"
- debugpackaging="${build.packaging.debug}"
- debugsigning="${build.signing.debug}"
+ apkfilepath="${out.packaged.file}"
+ debugpackaging="${build.is.packaging.debug}"
+ debugsigning="${build.is.signing.debug}"
abifilter="${filter.abi}"
verbose="${verbose}"
hascode="${manifest.hasCode}">
<dex path="${intermediate.dex.file}"/>
<sourcefolder path="${source.absolute.dir}"/>
- <sourcefolder refid="project.libraries.src"/>
- <jarfolder path="${jar.libs.absolute.dir}" />
- <jarfolder refid="project.libraries.libs" />
+ <jarfile refid="jar.libs.ref" />
<nativefolder path="${native.libs.absolute.dir}" />
<nativefolder refid="project.libraries.libs" />
<extra-jars/>
@@ -270,94 +310,245 @@
</sequential>
</macrodef>
- <!-- This is macro used only for sharing code among two targets, -install and
- -install-with-emma which do exactly the same but differ in dependencies -->
- <macrodef name="install-helper">
+ <macrodef name="run-tests-helper">
+ <attribute name="emma.enabled" default="false" />
+ <element name="extra-instrument-args" optional="yes" />
<sequential>
- <echo>Installing ${out.debug.file} onto default emulator or device...</echo>
+ <echo>Running tests ...</echo>
<exec executable="${adb}" failonerror="true">
<arg line="${adb.device.arg}" />
- <arg value="install" />
- <arg value="-r" />
- <arg path="${out.debug.file}" />
+ <arg value="shell" />
+ <arg value="am" />
+ <arg value="instrument" />
+ <arg value="-w" />
+ <arg value="-e" />
+ <arg value="coverage" />
+ <arg value="@{emma.enabled}" />
+ <extra-instrument-args />
+ <arg value="${manifest.package}/${test.runner}" />
</exec>
</sequential>
</macrodef>
- <!-- Rules -->
+ <macrodef name="record-build-info">
+ <attribute name="key" default="false" />
+ <attribute name="value" default="false" />
+ <sequential>
+ <propertyfile file="${out.build.prop.file}" comment="Last build type">
+ <entry key="@{key}" value="@{value}"/>
+ </propertyfile>
+ </sequential>
+ </macrodef>
- <!-- Creates the output directories if they don't exist yet. -->
- <target name="-dirs">
- <echo>Creating output directories if needed...</echo>
- <mkdir dir="${resource.absolute.dir}" />
- <mkdir dir="${jar.libs.absolute.dir}" />
- <mkdir dir="${out.absolute.dir}" />
- <if condition="${manifest.hasCode}">
+ <macrodef name="uninstall-helper">
+ <attribute name="app.package" default="false" />
+ <sequential>
+ <echo>Uninstalling @{app.package} from the default emulator or device...</echo>
+ <exec executable="${adb}" failonerror="true">
+ <arg line="${adb.device.arg}" />
+ <arg value="uninstall" />
+ <arg value="@{app.package}" />
+ </exec>
+ </sequential>
+ </macrodef>
+
+ <!-- ********** Build Targets ********** -->
+
+ <!-- this target simply force running -setup making
+ the project info be read. To be used as
+ ant all clean
+ to clean the main project as well as the libraries and tested project -->
+ <target name="all" depends="-setup"/>
+
+ <!-- clean target -->
+ <target name="clean" description="Removes output files created by other targets.">
+ <delete dir="${out.absolute.dir}" verbose="${verbose}" />
+ <delete dir="${gen.absolute.dir}" verbose="${verbose}" />
+
+ <!-- if we know about a tested project or libraries, we clean them too. This
+ will only work if the target 'all' was called first -->
+ <if condition="${project.is.test}">
<then>
- <mkdir dir="${gen.absolute.dir}" />
- <mkdir dir="${out.classes.absolute.dir}" />
+ <property name="tested.project.absolute.dir" location="${tested.project.dir}" />
+ <subant failonerror="true">
+ <fileset dir="${tested.project.absolute.dir}" includes="build.xml" />
+ <target name="all" />
+ <target name="clean" />
+ </subant>
+ </then>
+ </if>
+
+ <if>
+ <condition>
+ <isreference refid="project.libraries" />
+ </condition>
+ <then>
+ <subant
+ buildpathref="project.libraries"
+ antfile="build.xml"
+ failonerror="true">
+ <target name="all" />
+ <target name="clean" />
+ </subant>
</then>
</if>
</target>
- <!-- empty default pre-build target. Create a similar target in
- your build.xml and it'll be called instead of this one. -->
- <target name="-pre-build"/>
+ <!-- generic setup -->
+ <target name="-setup">
+ <echo>Gathering info for ${ant.project.name}...</echo>
+ <!-- load project properties, resolve Android target, library dependencies
+ and set some properties with the results.
+ All property names are passed as parameters ending in -Out -->
+ <setup
+ projectTypeOut="android.project.type"
+ androidJarFileOut="android.jar"
+ androidAidlFileOut="android.aidl"
+ renderScriptExeOut="renderscript"
+ renderScriptIncludeDirOut="android.rs"
+ bootclasspathrefOut="android.target.classpath"
+ projectLibrariesRootOut="project.libraries"
+ projectLibrariesJarsOut="project.libraries.jars"
+ projectLibrariesResOut="project.libraries.res"
+ projectLibrariesPackageOut="project.libraries.package"
+ projectLibrariesLibsOut="project.libraries.libs"
+ />
+
+ <!-- sets a few boolean based on android.project.type
+ to make the if task easier -->
+ <condition property="project.is.library" else="false">
+ <equals arg1="${android.project.type}" arg2="library" />
+ </condition>
+ <condition property="project.is.test" else="false">
+ <equals arg1="${android.project.type}" arg2="test" />
+ </condition>
+ </target>
- <!-- Generates the R.java file for this project's resources. -->
- <target name="-resource-src" depends="-dirs">
- <if condition="${manifest.hasCode}">
+ <!-- Pre build setup -->
+ <target name="-build-setup" depends="-setup">
+
+ <!-- read the previous build mode -->
+ <property file="${out.build.prop.file}" />
+ <!-- if empty the prop won't be set, so set it to the current target
+ to provide a default value equal to the current build -->
+ <property name="build.last.target" value="${build.target}" />
+ <!-- also set the default value for whether the build is instrumented -->
+ <property name="build.last.is.instrumented" value="${build.is.instrumented}" />
+
+ <!-- compile the libraries if any -->
+ <if>
+ <condition>
+ <isreference refid="project.libraries" />
+ </condition>
<then>
- <echo>Generating R.java / Manifest.java from the resources...</echo>
- <aapt executable="${aapt}"
- command="package"
- verbose="${verbose}"
- manifest="AndroidManifest.xml"
- androidjar="${android.jar}"
- rfolder="${gen.absolute.dir}">
- <res path="${resource.absolute.dir}" />
- </aapt>
+ <echo>Building Libraries</echo>
+ <subant
+ buildpathref="project.libraries"
+ antfile="build.xml"
+ target="${build.target}"
+ failonerror="true"/>
+ <echo></echo>
+ <echo>############################################</echo>
+ <echo>**** Back to project ${ant.project.name} ****</echo>
+ <echo>############################################</echo>
</then>
- <else>
- <echo>hasCode = false. Skipping...</echo>
- </else>
</if>
- </target>
- <!-- Generates java classes from .aidl files. -->
- <target name="-aidl" depends="-dirs">
- <if condition="${manifest.hasCode}">
+ <!-- compile the main project if this is a test project -->
+ <if condition="${project.is.test}">
<then>
- <echo>Compiling aidl files into Java classes...</echo>
- <aidl executable="${aidl}" framework="${android.aidl}"
- genFolder="${gen.absolute.dir}">
- <source path="${source.absolute.dir}"/>
- <source refid="project.libraries.src"/>
- </aidl>
+ <property name="tested.project.absolute.dir" location="${tested.project.dir}" />
+
+ <!-- figure out which target must be used to build the tested project.
+ If emma is enabled, then use 'instrument' otherwise, use 'debug' -->
+ <condition property="tested.project.target" value="instrument" else="debug">
+ <isset property="emma.enabled" />
+ </condition>
+
+ <echo>Building tested project at ${tested.project.absolute.dir}</echo>
+ <subant target="${tested.project.target}" failonerror="true">
+ <fileset dir="${tested.project.absolute.dir}" includes="build.xml" />
+ </subant>
+ <echo></echo>
+ <echo>############################################</echo>
+ <echo>**** Back to project ${ant.project.name} ****</echo>
+ <echo>############################################</echo>
</then>
- <else>
- <echo>hasCode = false. Skipping...</echo>
- </else>
</if>
- </target>
- <!-- Compiles RenderScript files into Java and bytecode. -->
- <target name="-renderscript" depends="-dirs">
- <if condition="${manifest.hasCode}">
+ <!-- Value of the hasCode attribute (Application node) extracted from manifest file -->
+ <xpath input="AndroidManifest.xml" expression="/manifest/application/@android:hasCode"
+ output="manifest.hasCode" default="true"/>
+
+ <!-- create a path with all the jar files, from the main project and the
+ libraries -->
+ <path id="jar.libs.ref">
+ <fileset dir="${jar.libs.absolute.dir}" includes="*.jar" />
+ <path refid="project.libraries.jars" />
+ </path>
+
+ <!-- special case for instrumented: if the previous build was
+ instrumented but not this one, clear out the compiled code -->
+ <if>
+ <condition>
+ <and>
+ <istrue value="${build.last.is.instrumented}" />
+ <isfalse value="${build.is.instrumented}" />
+ </and>
+ </condition>
<then>
- <echo>Compiling RenderScript files into Java classes and RenderScript bytecode...</echo>
- <renderscript executable="${renderscript}"
- framework="${android.rs}"
- genFolder="${gen.absolute.dir}"
- resFolder="${resource.absolute.dir}/raw">
- <source path="${source.absolute.dir}"/>
- <source refid="project.libraries.src"/>
- </renderscript>
+ <echo>Switching from instrumented to non-instrumented build.</echo>
+ <echo>Deleting previous compilation output:</echo>
+ <delete dir="${out.classes.absolute.dir}" verbose="${verbose}" />
</then>
- <else>
- <echo>hasCode = false. Skipping...</echo>
- </else>
</if>
+
+ <echo>Creating output directories if needed...</echo>
+ <mkdir dir="${resource.absolute.dir}" />
+ <mkdir dir="${jar.libs.absolute.dir}" />
+ <mkdir dir="${out.absolute.dir}" />
+ <mkdir dir="${out.res.absolute.dir}" />
+ <do-only-if-manifest-hasCode>
+ <mkdir dir="${gen.absolute.dir}" />
+ <mkdir dir="${out.classes.absolute.dir}" />
+ </do-only-if-manifest-hasCode>
+ </target>
+
+ <!-- empty default pre-build target. Create a similar target in
+ your build.xml and it'll be called instead of this one. -->
+ <target name="-pre-build"/>
+
+ <!-- Code Generation: compile resources (aapt -> R.java), aidl, renderscript -->
+ <target name="-code-gen">
+ <do-only-if-manifest-hasCode
+ elseText="hasCode = false. Skipping aidl/renderscript/R.java">
+ <echo>Compiling aidl files into Java classes...</echo>
+ <aidl executable="${aidl}" framework="${android.aidl}"
+ genFolder="${gen.absolute.dir}">
+ <source path="${source.absolute.dir}"/>
+ </aidl>
+
+ <!-- renderscript generates resources so it must be called before aapt -->
+ <echo>Compiling RenderScript files into Java classes and RenderScript bytecode...</echo>
+ <renderscript executable="${renderscript}"
+ framework="${android.rs}"
+ genFolder="${gen.absolute.dir}"
+ resFolder="${resource.absolute.dir}/raw">
+ <source path="${source.absolute.dir}"/>
+ </renderscript>
+
+ <echo>Generating R.java / Manifest.java from the resources...</echo>
+ <aapt executable="${aapt}"
+ command="package"
+ verbose="${verbose}"
+ manifest="AndroidManifest.xml"
+ androidjar="${android.jar}"
+ rfolder="${gen.absolute.dir}"
+ projectLibrariesResName="project.libraries.res"
+ projectLibrariesPackageName="project.libraries.package">
+ <res path="${resource.absolute.dir}" />
+ </aapt>
+ </do-only-if-manifest-hasCode>
</target>
<!-- empty default pre-compile target. Create a similar target in
@@ -365,42 +556,71 @@
<target name="-pre-compile"/>
<!-- Compiles this project's .java files into .class files. -->
- <target name="compile" depends="-pre-build, -aidl, -renderscript, -resource-src, -pre-compile"
- description="Compiles project's .java files into .class files">
- <if condition="${manifest.hasCode}">
- <then>
- <!-- If android rules are used for a test project, its classpath should include
- tested project's location -->
- <condition property="extensible.classpath"
- value="${tested.project.absolute.dir}/bin/classes"
- else=".">
- <isset property="tested.project.absolute.dir" />
- </condition>
- <condition property="extensible.libs.classpath"
- value="${tested.project.absolute.dir}/libs"
- else="${jar.libs.dir}">
- <isset property="tested.project.absolute.dir" />
- </condition>
- <javac encoding="${java.encoding}"
- source="${java.source}" target="${java.target}"
- debug="true" extdirs=""
- destdir="${out.classes.absolute.dir}"
- bootclasspathref="android.target.classpath"
- verbose="${verbose}"
- classpath="${extensible.classpath}"
- classpathref="jar.libs.ref">
- <src path="${source.absolute.dir}" />
- <src path="${gen.absolute.dir}" />
- <src refid="project.libraries.src" />
- <classpath>
- <fileset dir="${extensible.libs.classpath}" includes="*.jar" />
- </classpath>
- </javac>
- </then>
- <else>
- <echo>hasCode = false. Skipping...</echo>
- </else>
- </if>
+ <target name="-compile" depends="-build-setup, -pre-build, -code-gen, -pre-compile">
+ <do-only-if-manifest-hasCode elseText="hasCode = false. Skipping...">
+ <!-- If android rules are used for a test project, its classpath should include
+ tested project's location -->
+ <condition property="extensible.classpath"
+ value="${tested.project.absolute.dir}/bin/classes"
+ else=".">
+ <isset property="tested.project.absolute.dir" />
+ </condition>
+ <condition property="extensible.libs.classpath"
+ value="${tested.project.absolute.dir}/${jar.libs.dir}"
+ else="${jar.libs.dir}">
+ <isset property="tested.project.absolute.dir" />
+ </condition>
+ <javac encoding="${java.encoding}"
+ source="${java.source}" target="${java.target}"
+ debug="true" extdirs=""
+ destdir="${out.classes.absolute.dir}"
+ bootclasspathref="android.target.classpath"
+ verbose="${verbose}"
+ classpath="${extensible.classpath}"
+ classpathref="jar.libs.ref">
+ <src path="${source.absolute.dir}" />
+ <src path="${gen.absolute.dir}" />
+ <classpath>
+ <fileset dir="${extensible.libs.classpath}" includes="*.jar" />
+ </classpath>
+ </javac>
+ <!-- if the project is a library then we generate a jar file -->
+ <if condition="${project.is.library}">
+ <then>
+ <echo>Creating library output jar file...</echo>
+ <property name="out.library.jar.file" location="${out.absolute.dir}/classes.jar" />
+ <if>
+ <condition>
+ <length string="${android.package.excludes}" trim="true" when="greater" length="0" />
+ </condition>
+ <then>
+ <echo>Custom jar packaging exclusion: ${android.package.excludes}</echo>
+ </then>
+ </if>
+ <jar destfile="${out.library.jar.file}">
+ <fileset dir="${out.classes.absolute.dir}" excludes="**/R.class **/R$*.class"/>
+ <fileset dir="${source.absolute.dir}" excludes="**/*.java ${android.package.excludes}" />
+ </jar>
+ </then>
+ </if>
+
+ <!-- if the project is instrumented, intrument the classes -->
+ <if condition="${build.is.instrumented}">
+ <then>
+ <echo>Instrumenting classes from ${out.absolute.dir}/classes...</echo>
+ <!-- It only instruments class files, not any external libs -->
+ <emma enabled="true">
+ <instr verbosity="${verbosity}"
+ mode="overwrite"
+ instrpath="${out.absolute.dir}/classes"
+ outdir="${out.absolute.dir}/classes">
+ </instr>
+ <!-- TODO: exclusion filters on R*.class and allowing custom exclusion from
+ user defined file -->
+ </emma>
+ </then>
+ </if>
+ </do-only-if-manifest-hasCode>
</target>
<!-- empty default post-compile target. Create a similar target in
@@ -418,13 +638,12 @@
-debug-obfuscation-check
Obfuscation should not happen. Set the same property to false.
-obfuscate
- ** Make sure unless="do.not.compile" is used in the target definition **
check if the property set in -debug/release-obfuscation-check is set to true.
If true:
Perform obfuscation
Set property out.dex.input.absolute.dir to be the output of the obfuscation
-->
- <target name="-obfuscate" unless="do.not.compile">
+ <target name="-obfuscate">
<if condition="${proguard.enabled}">
<then>
<property name="obfuscate.absolute.dir" location="${out.absolute.dir}/proguard" />
@@ -469,7 +688,7 @@
<mkdir dir="${obfuscate.absolute.dir}" />
<delete file="${preobfuscate.jar.file}"/>
<delete file="${obfuscated.jar.file}"/>
- <jar basedir="${out.classes.dir}"
+ <jar basedir="${out.classes.absolute.dir}"
destfile="${preobfuscate.jar.file}" />
<proguard>
@${proguard.config}
@@ -486,28 +705,44 @@
</target>
<!-- Converts this project's .class files into .dex files -->
- <target name="-dex" depends="compile, -post-compile, -obfuscate"
- unless="do.not.compile">
- <if condition="${manifest.hasCode}">
- <then>
- <dex-helper />
- </then>
- <else>
- <echo>hasCode = false. Skipping...</echo>
- </else>
- </if>
+ <target name="-dex" depends="-compile, -post-compile, -obfuscate">
+ <do-only-if-manifest-hasCode elseText="hasCode = false. Skipping...">
+ <!-- only convert to dalvik bytecode is *not* a library -->
+ <do-only-if-not-library elseText="Library project: do not convert bytecode..." >
+ <!-- special case for instrumented builds: need to use no-locals and need
+ to pass in the emma jar. -->
+ <if condition="${build.is.instrumented}">
+ <then>
+ <dex-helper>
+ <extra-parameters>
+ <arg value="--no-locals" />
+ </extra-parameters>
+ <external-libs>
+ <fileset file="${emma.dir}/emma_device.jar" />
+ </external-libs>
+ </dex-helper>
+ </then>
+ <else>
+ <dex-helper />
+ </else>
+ </if>
+ </do-only-if-not-library>
+ </do-only-if-manifest-hasCode>
</target>
<!-- Updates the pre-processed PNG cache -->
<target name="-crunch">
- <exec executable="${aapt}">
- <arg value="crunch" />
- <arg value="-v" />
- <arg value="-S" />
- <arg path="${resource.absolute.dir}" />
- <arg value="-C" />
- <arg path="${out.resource.absolute.dir}" />
- </exec>
+ <!-- only crunch if *not* a library project -->
+ <do-only-if-not-library elseText="Library project: do not optimize PNGs..." >
+ <exec executable="${aapt}" taskName="crunch">
+ <arg value="crunch" />
+ <arg value="-v" />
+ <arg value="-S" />
+ <arg path="${resource.absolute.dir}" />
+ <arg value="-C" />
+ <arg path="${out.res.absolute.dir}" />
+ </exec>
+ </do-only-if-not-library>
</target>
<!-- Puts the project's resources into the output package file
@@ -516,43 +751,53 @@
declared in default.properties.
-->
<target name="-package-resources" depends="-crunch">
- <echo>Packaging resources</echo>
- <aapt executable="${aapt}"
- command="package"
- versioncode="${version.code}"
- versionname="${version.name}"
- debug="${build.packaging.debug}"
- manifest="AndroidManifest.xml"
- assets="${asset.absolute.dir}"
- androidjar="${android.jar}"
- apkfolder="${out.absolute.dir}"
- nocrunch="${build.packaging.nocrunch}"
- resourcefilename="${resource.package.file.name}"
- resourcefilter="${aapt.resource.filter}">
- <res path="${out.resource.absolute.dir}" />
- <res path="${resource.absolute.dir}" />
- <!-- <nocompress /> forces no compression on any files in assets or res/raw -->
- <!-- <nocompress extension="xml" /> forces no compression on specific file extensions in assets and res/raw -->
- </aapt>
+ <!-- only package resources if *not* a library project -->
+ <do-only-if-not-library elseText="Library project: do not package apk..." >
+ <aapt executable="${aapt}"
+ command="package"
+ versioncode="${version.code}"
+ versionname="${version.name}"
+ debug="${build.is.packaging.debug}"
+ manifest="AndroidManifest.xml"
+ assets="${asset.absolute.dir}"
+ androidjar="${android.jar}"
+ apkfolder="${out.absolute.dir}"
+ nocrunch="${build.packaging.nocrunch}"
+ resourcefilename="${resource.package.file.name}"
+ resourcefilter="${aapt.resource.filter}"
+ projectLibrariesResName="project.libraries.res"
+ projectLibrariesPackageName="project.libraries.package"
+ previousBuildType="${build.last.target}"
+ buildType="${build.target}">
+ <res path="${out.res.absolute.dir}" />
+ <res path="${resource.absolute.dir}" />
+ <!-- <nocompress /> forces no compression on any files in assets or res/raw -->
+ <!-- <nocompress extension="xml" /> forces no compression on specific file extensions in assets and res/raw -->
+ </aapt>
+ </do-only-if-not-library>
</target>
- <!-- Packages the application and sign it with a debug key. -->
- <target name="-package-debug-sign" depends="-dex, -package-resources">
- <package-helper
- output.filepath="${out.debug.unaligned.file}" />
- </target>
-
- <!-- Packages the application without signing it. -->
- <target name="-package-release" depends="-dex, -package-resources">
- <package-helper
- output.filepath="${out.unsigned.file}"/>
+ <!-- Packages the application. -->
+ <target name="-package" depends="-dex, -package-resources">
+ <!-- only package apk if *not* a library project -->
+ <do-only-if-not-library elseText="Library project: do not package apk..." >
+ <if condition="${build.is.instrumented}">
+ <then>
+ <package-helper>
+ <extra-jars>
+ <!-- Injected from external file -->
+ <jarfile path="${emma.dir}/emma_device.jar" />
+ </extra-jars>
+ </package-helper>
+ </then>
+ <else>
+ <package-helper />
+ </else>
+ </if>
+ </do-only-if-not-library>
</target>
- <target name="-compile-tested-if-test" if="tested.project.dir" unless="do.not.compile.again">
- <subant target="compile">
- <fileset dir="${tested.project.absolute.dir}" includes="build.xml" />
- </subant>
- </target>
+ <!-- ********** Debug specific targets ********** -->
<target name="-debug-obfuscation-check">
<!-- proguard is never enabled in debug mode -->
@@ -560,27 +805,38 @@
</target>
<target name="-set-debug-mode" depends="-debug-obfuscation-check">
- <!-- property only set in debug mode.
- Useful for if/unless attributes in target node
- when using Ant before 1.8 -->
- <property name="build.mode.debug" value="true"/>
+ <property name="out.packaged.file" location="${out.absolute.dir}/${ant.project.name}-debug-unaligned.apk" />
+ <property name="out.final.file" location="${out.absolute.dir}/${ant.project.name}-debug.apk" />
+
+ <!-- record the current build target -->
+ <property name="build.target" value="debug" />
+
+ <property name="build.is.instrumented" value="false" />
<!-- whether the build is a debug build. always set. -->
- <property name="build.packaging.debug" value="true" />
+ <property name="build.is.packaging.debug" value="true" />
<!-- signing mode: debug -->
- <property name="build.signing.debug" value="true" />
+ <property name="build.is.signing.debug" value="true" />
</target>
- <!-- Builds debug output package, provided all the necessary files are already dexed -->
- <target name="debug" depends="-set-debug-mode, -compile-tested-if-test, -package-debug-sign"
+ <!-- Builds debug output package -->
+ <target name="debug" depends="-set-debug-mode, -package"
description="Builds the application and signs it with a debug key.">
- <zipalign-helper in.package="${out.debug.unaligned.file}"
- out.package="${out.debug.file}" />
- <echo>Debug Package: ${out.debug.file}</echo>
+ <!-- only create apk if *not* a library project -->
+ <do-only-if-not-library elseText="Library project: do not create apk..." >
+ <sequential>
+ <zipalign-helper in.package="${out.packaged.file}" out.package="${out.final.file}" />
+ <echo>Debug Package: ${out.final.file}</echo>
+ </sequential>
+ </do-only-if-not-library>
+ <record-build-info key="build.last.target" value="${build.target}" />
+ <record-build-info key="build.last.is.instrumented" value="${build.is.instrumented}" />
</target>
+ <!-- ********** Release specific targets ********** -->
+
<!-- called through target 'release'. Only executed if the keystore and
key alias are known but not their password. -->
<target name="-release-prompt-for-password" if="has.keystore" unless="has.password">
@@ -596,15 +852,22 @@
<!-- called through target 'release'. Only executed if there's no
keystore/key alias set -->
<target name="-release-nosign" unless="has.keystore">
- <echo>No key.store and key.alias properties found in build.properties.</echo>
- <echo>Please sign ${out.unsigned.file} manually</echo>
- <echo>and run zipalign from the Android SDK tools.</echo>
+ <!-- no release builds for library project -->
+ <do-only-if-not-library elseText="" >
+ <sequential>
+ <echo>No key.store and key.alias properties found in build.properties.</echo>
+ <echo>Please sign ${out.packaged.file} manually</echo>
+ <echo>and run zipalign from the Android SDK tools.</echo>
+ </sequential>
+ </do-only-if-not-library>
+ <record-build-info key="build.last.target" value="${build.target}" />
+ <record-build-info key="build.last.is.instrumented" value="${build.is.instrumented}" />
</target>
<target name="-release-obfuscation-check">
<condition property="proguard.enabled" value="true" else="false">
<and>
- <isset property="build.mode.release" />
+ <isset property="build.is.mode.release" />
<isset property="proguard.config" />
</and>
</condition>
@@ -618,16 +881,23 @@
</target>
<target name="-set-release-mode">
+ <property name="out.packaged.file" location="${out.absolute.dir}/${ant.project.name}-release-unsigned.apk" />
+ <property name="out.final.file" location="${out.absolute.dir}/${ant.project.name}-release.apk" />
+
+ <!-- record the current build target -->
+ <property name="build.target" value="release" />
+
+ <property name="build.is.instrumented" value="false" />
+
<!-- release mode is only valid if the manifest does not explicitly
- set debuggable to true. default is false.
- We actually store build.packaging.debug, not build.release -->
+ set debuggable to true. default is false. -->
<xpath input="AndroidManifest.xml" expression="/manifest/application/@android:debuggable"
- output="build.packaging.debug" default="false"/>
+ output="build.is.packaging.debug" default="false"/>
<!-- signing mode: release -->
- <property name="build.signing.debug" value="false" />
+ <property name="build.is.signing.debug" value="false" />
- <if condition="${build.packaging.debug}">
+ <if condition="${build.is.packaging.debug}">
<then>
<echo>*************************************************</echo>
<echo>**** Android Manifest has debuggable=true ****</echo>
@@ -638,7 +908,7 @@
<!-- property only set in release mode.
Useful for if/unless attributes in target node
when using Ant before 1.8 -->
- <property name="build.mode.release" value="true"/>
+ <property name="build.is.mode.release" value="true"/>
</else>
</if>
</target>
@@ -647,111 +917,257 @@
only if release-sign is true (set in -release-check,
called by -release-no-sign)-->
<target name="release"
- depends="-set-release-mode, -release-obfuscation-check, -package-release, -release-prompt-for-password, -release-nosign"
+ depends="-set-release-mode, -release-obfuscation-check, -package, -release-prompt-for-password, -release-nosign"
if="has.keystore"
description="Builds the application. The generated apk file must be signed before
it is published.">
- <!-- Signs the APK -->
- <echo>Signing final apk...</echo>
- <signjar
- jar="${out.unsigned.file}"
- signedjar="${out.unaligned.file}"
- keystore="${key.store}"
- storepass="${key.store.password}"
- alias="${key.alias}"
- keypass="${key.alias.password}"
- verbose="${verbose}" />
-
- <!-- Zip aligns the APK -->
- <zipalign-helper in.package="${out.unaligned.file}"
- out.package="${out.release.file}" />
- <echo>Release Package: ${out.release.file}</echo>
- </target>
- <target name="install" depends="debug"
- description="Installs/reinstalls the debug package onto a running
- emulator or device. If the application was previously installed,
- the signatures must match." >
- <install-helper />
+ <!-- only create apk if *not* a library project -->
+ <do-only-if-not-library elseText="Library project: do not create apk..." >
+ <sequential>
+ <property name="out.unaligned.file" location="${out.absolute.dir}/${ant.project.name}-release-unaligned.apk" />
+
+ <!-- Signs the APK -->
+ <echo>Signing final apk...</echo>
+ <signjar
+ jar="${out.packaged.file}"
+ signedjar="${out.unaligned.file}"
+ keystore="${key.store}"
+ storepass="${key.store.password}"
+ alias="${key.alias}"
+ keypass="${key.alias.password}"
+ verbose="${verbose}" />
+
+ <!-- Zip aligns the APK -->
+ <zipalign-helper in.package="${out.unaligned.file}"
+ out.package="${out.final.file}" />
+ <echo>Release Package: ${out.final.file}</echo>
+ </sequential>
+ </do-only-if-not-library>
+ <record-build-info key="build.last.target" value="${build.target}" />
+ <record-build-info key="build.last.is.instrumented" value="${build.is.instrumented}" />
</target>
- <target name="-uninstall-check">
- <condition property="uninstall.run">
- <isset property="manifest.package" />
- </condition>
- </target>
+ <!-- ********** Instrumented specific targets ********** -->
+
+ <!-- These targets are specific for the project under test when it
+ gets compiled by the test projects in a way that will make it
+ support emma code coverage -->
+
+ <target name="-set-instrumented-mode">
+ <property name="out.packaged.file" location="${out.absolute.dir}/${ant.project.name}-instrumented-unaligned.apk" />
+ <property name="out.final.file" location="${out.absolute.dir}/${ant.project.name}-instrumented.apk" />
- <target name="-uninstall-error" depends="-uninstall-check" unless="uninstall.run">
- <echo>Unable to run 'ant uninstall', manifest.package property is not defined.
- </echo>
+ <!-- whether the build is an instrumented build. -->
+ <property name="build.is.instrumented" value="true" />
</target>
- <!-- Uninstalls the package from the default emulator/device -->
- <target name="uninstall" depends="-uninstall-error" if="uninstall.run"
- description="Uninstalls the application from a running emulator or device.">
- <echo>Uninstalling ${manifest.package} from the default emulator or device...</echo>
- <exec executable="${adb}" failonerror="true">
- <arg line="${adb.device.arg}" />
- <arg value="uninstall" />
- <arg value="${manifest.package}" />
- </exec>
+ <!-- Builds instrumented output package -->
+ <target name="instrument" depends="-set-instrumented-mode, debug"
+ description="Builds an instrumented packaged.">
+ <!-- only create apk if *not* a library project -->
+ <do-only-if-not-library elseText="Library project: do not create apk..." >
+ <sequential>
+ <zipalign-helper in.package="${out.packaged.file}" out.package="${out.final.file}" />
+ <echo>Instrumented Package: ${out.final.file}</echo>
+ </sequential>
+ </do-only-if-not-library>
+ <record-build-info key="build.last.target" value="${build.target}" />
+ <record-build-info key="build.last.is.instrumented" value="${build.is.instrumented}" />
</target>
- <target name="clean" description="Removes output files created by other targets.">
- <delete dir="${out.absolute.dir}" verbose="${verbose}" />
- <delete dir="${gen.absolute.dir}" verbose="${verbose}" />
+ <!-- ********** Test project specific targets ********** -->
+
+ <!-- enable code coverage -->
+ <target name="emma">
+ <property name="emma.enabled" value="true" />
</target>
- <!-- Targets for code-coverage measurement purposes, invoked from external file -->
-
- <!-- Emma-instruments tested project classes (compiles the tested project if necessary)
- and writes instrumented classes to ${instrumentation.absolute.dir}/classes -->
- <target name="-emma-instrument" depends="compile">
- <echo>Instrumenting classes from ${out.absolute.dir}/classes...</echo>
- <!-- It only instruments class files, not any external libs -->
- <emma enabled="true">
- <instr verbosity="${verbosity}"
- mode="overwrite"
- instrpath="${out.absolute.dir}/classes"
- outdir="${out.absolute.dir}/classes">
- </instr>
- <!-- TODO: exclusion filters on R*.class and allowing custom exclusion from
- user defined file -->
- </emma>
+ <!-- fails if the project is not a test project -->
+ <target name="-test-project-check">
+ <!-- can't use project.is.test since the setup target is not run -->
+ <if>
+ <condition>
+ <isset property="tested.project.dir" />
+ </condition>
+ <else>
+ <fail message="Project is not a test project." />
+ </else>
+ </if>
</target>
- <target name="-dex-instrumented" depends="-emma-instrument">
- <dex-helper>
- <extra-parameters>
- <arg value="--no-locals" />
- </extra-parameters>
- <external-libs>
- <fileset file="${emma.dir}/emma_device.jar" />
- </external-libs>
- </dex-helper>
+ <!-- Installs the tested project. This make sure to install the proper package based on
+ the value of emma.enabled -->
+ <target name="-install-tested-project" depends="-test-project-check">
+ <!-- figure out which tested package to install based on emma.enabled -->
+ <condition property="tested.project.install.target" value="installi" else="installd">
+ <isset property="emma.enabled" />
+ </condition>
+ <subant target="${tested.project.install.target}" failonerror="true">
+ <fileset dir="${tested.project.absolute.dir}" includes="build.xml" />
+ </subant>
</target>
- <!-- Invoked from external files for code coverage purposes -->
- <target name="-package-with-emma" depends="-dex-instrumented, -package-resources">
- <package-helper
- output.filepath="${out.debug.unaligned.file}">
- <extra-jars>
- <!-- Injected from external file -->
- <jarfile path="${emma.dir}/emma_device.jar" />
- </extra-jars>
- </package-helper>
+ <target name="test" depends="-test-project-check"
+ description="Runs tests from the package defined in test.package property">
+
+ <property name="tested.project.absolute.dir" location="${tested.project.dir}" />
+
+ <property name="test.runner" value="android.test.InstrumentationTestRunner" />
+
+ <!-- Application package of the tested project extracted from its manifest file -->
+ <xpath input="${tested.project.absolute.dir}/AndroidManifest.xml"
+ expression="/manifest/@package" output="tested.manifest.package" />
+
+ <property name="emma.dump.file"
+ value="/sdcard/${tested.manifest.package}_coverage.ec" />
+
+ <if condition="${emma.enabled}">
+ <then>
+ <fail message="code coverage is not currently supported. coming soon"/>
+ <run-tests-helper emma.enabled="true">
+ <extra-instrument-args>
+ <arg value="-e" />
+ <arg value="coverageFile" />
+ <arg value="${emma.dump.file}" />
+ </extra-instrument-args>
+ </run-tests-helper>
+ <echo>Downloading coverage file into project directory...</echo>
+ <exec executable="${adb}" failonerror="true">
+ <arg line="${adb.device.arg}" />
+ <arg value="pull" />
+ <arg value="${emma.dump.file}" />
+ <arg value="coverage.ec" />
+ </exec>
+ <echo>Extracting coverage report...</echo>
+ <emma>
+ <report sourcepath="${tested.project.absolute.dir}/${source.dir}"
+ verbosity="${verbosity}">
+ <!-- TODO: report.dir or something like should be introduced if necessary -->
+ <infileset dir=".">
+ <include name="coverage.ec" />
+ <include name="coverage.em" />
+ </infileset>
+ <!-- TODO: reports in other, indicated by user formats -->
+ <html outfile="coverage.html" />
+ </report>
+ </emma>
+ <echo>Cleaning up temporary files...</echo>
+ <delete file="coverage.ec" />
+ <delete file="coverage.em" />
+ <echo>Saving the report file in ${basedir}/coverage/coverage.html</echo>
+ </then>
+ <else>
+ <run-tests-helper />
+ </else>
+ </if>
</target>
- <target name="-debug-with-emma" depends="-set-debug-mode, -package-with-emma">
- <zipalign-helper in.package="${out.debug.unaligned.file}"
- out.package="${out.debug.file}" />
+
+ <!-- ********** Install/uninstall specific targets ********** -->
+
+ <target name="install"
+ description="Installs the newly build package. Must be used in conjunction with a build target
+ (debug/release/instrument). If the application was previously installed, the application
+ is reinstalled if the signature matches." >
+ <!-- only do install if *not* a library project -->
+ <do-only-if-not-library elseText="Library project: nothing to install!" >
+ <if>
+ <condition>
+ <isset property="out.final.file" />
+ </condition>
+ <then>
+ <if>
+ <condition>
+ <resourceexists>
+ <file file="${out.final.file}"/>
+ </resourceexists>
+ </condition>
+ <then>
+ <echo>Installing ${out.final.file} onto default emulator or device...</echo>
+ <exec executable="${adb}" failonerror="true">
+ <arg line="${adb.device.arg}" />
+ <arg value="install" />
+ <arg value="-r" />
+ <arg path="${out.final.file}" />
+ </exec>
+ </then>
+ <else>
+ <fail message="File {out.final.file} does not exist." />
+ </else>
+ </if>
+ </then>
+ <else>
+ <echo>Install file not specified.</echo>
+ <echo></echo>
+ <echo>'ant install' now requires the build target to be specified as well.</echo>
+ <echo></echo>
+ <echo></echo>
+ <echo> ant debug install</echo>
+ <echo> ant release install</echo>
+ <echo> ant instrument install</echo>
+ <echo>This will build the given package and install it.</echo>
+ <echo></echo>
+ <echo>Alternatively, you can use</echo>
+ <echo> ant installd</echo>
+ <echo> ant installr</echo>
+ <echo> ant installi</echo>
+ <echo> ant installt</echo>
+ <echo>to only install an existing package (this will not rebuild the package.)</echo>
+ <fail />
+ </else>
+ </if>
+ </do-only-if-not-library>
</target>
- <target name="-install-with-emma" depends="-debug-with-emma">
- <install-helper />
+ <target name="installd" depends="-set-debug-mode, install"
+ description="Installs (only) the debug package." />
+ <target name="installr" depends="-set-release-mode, install"
+ description="Installs (only) the release package." />
+ <target name="installi" depends="-set-instrumented-mode, install"
+ description="Installs (only) the instrumented package." />
+ <target name="installt" depends="-install-tested-project, installd"
+ description="Installs (only) the test and tested packages." />
+
+
+ <!-- Uninstalls the package from the default emulator/device -->
+ <target name="uninstall"
+ description="Uninstalls the application from a running emulator or device.">
+ <if>
+ <condition>
+ <isset property="manifest.package" />
+ </condition>
+ <then>
+ <uninstall-helper app.package="${manifest.package}" />
+ </then>
+ <else>
+ <echo>Could not find application package in manifest. Cannot run 'adb uninstall'.</echo>
+ </else>
+ </if>
+
+ <if condition="${project.is.test}">
+ <then>
+ <property name="tested.project.absolute.dir" location="${tested.project.dir}" />
+
+ <!-- Application package of the tested project extracted from its manifest file -->
+ <xpath input="${tested.project.absolute.dir}/AndroidManifest.xml"
+ expression="/manifest/@package" output="tested.manifest.package" />
+ <if>
+ <condition>
+ <isset property="tested.manifest.package" />
+ </condition>
+ <then>
+ <uninstall-helper app.package="${tested.manifest.package}" />
+ </then>
+ <else>
+ <echo>Could not find tested application package in manifest. Cannot run 'adb uninstall'.</echo>
+ </else>
+ </if>
+ </then>
+ </if>
+
</target>
- <!-- End of targets for code-coverage measurement purposes -->
<target name="help">
<!-- displays starts at col 13
@@ -759,14 +1175,29 @@
<echo>Android Ant Build. Available targets:</echo>
<echo> help: Displays this help.</echo>
<echo> clean: Removes output files created by other targets.</echo>
- <echo> compile: Compiles project's .java files into .class files.</echo>
+ <echo> The 'all' target can be used to clean dependencies</echo>
+ <echo> (tested projects and libraries)at the same time</echo>
+ <echo> using: 'ant all clean'</echo>
<echo> debug: Builds the application and signs it with a debug key.</echo>
<echo> release: Builds the application. The generated apk file must be</echo>
<echo> signed before it is published.</echo>
- <echo> install: Installs/reinstalls the debug package onto a running</echo>
- <echo> emulator or device.</echo>
+ <echo> instrument:Builds an instrumented package and signs it with a</echo>
+ <echo> debug key.</echo>
+ <echo> test: Runs the tests. Project must be a test project and</echo>
+ <echo> must have been built. Typical usage would be:</echo>
+ <echo> ant [emma] debug installt test</echo>
+ <echo> emma: Transiently enables code coverage for subsequent</echo>
+ <echo> targets.</echo>
+ <echo> install: Installs the newly build package. Must either be used</echo>
+ <echo> in conjunction with a build target (debug/release/</echo>
+ <echo> instrument) or with the proper suffix indicating</echo>
+ <echo> which package to install (see below).</echo>
<echo> If the application was previously installed, the</echo>
- <echo> signatures must match.</echo>
+ <echo> application is reinstalled if the signature matches.</echo>
+ <echo> installd: Installs (only) the debug package.</echo>
+ <echo> installr: Installs (only) the release package.</echo>
+ <echo> installi: Installs (only) the instrumented package.</echo>
+ <echo> installt: Installs (only) the test and tested packages.</echo>
<echo> uninstall: Uninstalls the application from a running emulator or</echo>
<echo> device.</echo>
</target>
diff --git a/files/ant/pre_setup.xml b/files/ant/pre_setup.xml
index eee449a..1c86109 100644
--- a/files/ant/pre_setup.xml
+++ b/files/ant/pre_setup.xml
@@ -1,14 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="imported" basedir=".">
- <!-- Custom Android task to deal with the project target, and import the
- proper rules.
- This requires ant 1.6.0 or above. -->
- <path id="android.antlibs">
- <pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
- </path>
+ <fail message="Your build.xml file is outdated. Delete it and regenerate it with 'android update project'"/>
- <taskdef name="setup"
- classname="com.android.ant.SetupTask"
- classpathref="android.antlibs" />
</project>
diff --git a/files/ant/test_rules.xml b/files/ant/test_rules.xml
deleted file mode 100644
index 955fba4..0000000
--- a/files/ant/test_rules.xml
+++ /dev/null
@@ -1,106 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project name="android_test_rules" default="run-tests">
-
- <import file="main_rules.xml" />
-
- <property name="tested.project.absolute.dir" location="${tested.project.dir}" />
- <property name="instrumentation.dir" value="instrumented" />
- <property name="instrumentation.absolute.dir" location="${instrumentation.dir}" />
-
- <property name="test.runner" value="android.test.InstrumentationTestRunner" />
- <!-- Application package of the tested project extracted from its manifest file -->
- <xpath input="${tested.project.absolute.dir}/AndroidManifest.xml"
- expression="/manifest/@package" output="tested.manifest.package" />
-
- <!-- TODO: make it more configurable in the next CL's - now it is default for auto-generated
- project -->
- <property name="emma.dump.file"
- value="/data/data/${tested.manifest.package}/files/coverage.ec" />
-
- <macrodef name="run-tests-helper">
- <attribute name="emma.enabled" default="false" />
- <element name="extra-instrument-args" optional="yes" />
- <sequential>
- <echo>Running tests ...</echo>
- <exec executable="${adb}" failonerror="true">
- <arg line="${adb.device.arg}" />
- <arg value="shell" />
- <arg value="am" />
- <arg value="instrument" />
- <arg value="-w" />
- <arg value="-e" />
- <arg value="coverage" />
- <arg value="@{emma.enabled}" />
- <extra-instrument-args />
- <arg value="${manifest.package}/${test.runner}" />
- </exec>
- </sequential>
- </macrodef>
-
- <!-- Invoking this target sets the value of extensible.classpath, which is being added to javac
- classpath in target 'compile' (android_rules.xml) -->
- <target name="-set-coverage-classpath">
- <property name="extensible.classpath"
- location="${instrumentation.absolute.dir}/classes" />
- </target>
-
- <!-- Ensures that tested project is installed on the device before we run the tests.
- Used for ordinary tests, without coverage measurement -->
- <target name="-install-tested-project">
- <property name="do.not.compile.again" value="true" />
- <subant target="install">
- <fileset dir="${tested.project.absolute.dir}" includes="build.xml" />
- </subant>
- </target>
-
- <target name="run-tests" depends="-install-tested-project, install"
- description="Runs tests from the package defined in test.package property">
- <run-tests-helper />
- </target>
-
- <target name="-install-instrumented">
- <property name="do.not.compile.again" value="true" />
- <subant target="-install-with-emma">
- <property name="out.absolute.dir" value="${instrumentation.absolute.dir}" />
- <fileset dir="${tested.project.absolute.dir}" includes="build.xml" />
- </subant>
- </target>
-
- <target name="coverage" depends="-set-coverage-classpath, -install-instrumented, install"
- description="Runs the tests against the instrumented code and generates
- code coverage report">
- <run-tests-helper emma.enabled="true">
- <extra-instrument-args>
- <arg value="-e" />
- <arg value="coverageFile" />
- <arg value="${emma.dump.file}" />
- </extra-instrument-args>
- </run-tests-helper>
- <echo>Downloading coverage file into project directory...</echo>
- <exec executable="${adb}" failonerror="true">
- <arg line="${adb.device.arg}" />
- <arg value="pull" />
- <arg value="${emma.dump.file}" />
- <arg value="coverage.ec" />
- </exec>
- <echo>Extracting coverage report...</echo>
- <emma>
- <report sourcepath="${tested.project.absolute.dir}/${source.dir}"
- verbosity="${verbosity}">
- <!-- TODO: report.dir or something like should be introduced if necessary -->
- <infileset dir=".">
- <include name="coverage.ec" />
- <include name="coverage.em" />
- </infileset>
- <!-- TODO: reports in other, indicated by user formats -->
- <html outfile="coverage.html" />
- </report>
- </emma>
- <echo>Cleaning up temporary files...</echo>
- <delete dir="${instrumentation.absolute.dir}" />
- <delete file="coverage.ec" />
- <delete file="coverage.em" />
- <echo>Saving the report file in ${basedir}/coverage/coverage.html</echo>
- </target>
-
-</project>
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java
index 44c46ee..9c369ed 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java
@@ -61,9 +61,13 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> {
public final static int SOURCES = 18;
/** OS Path to the target specific docs */
public final static int DOCS = 19;
- /** OS Path to the target's version of the aapt tool. */
+ /** OS Path to the target's version of the aapt tool.
+ * This is deprecated as aapt is now in the platform tools and not in the platform. */
+ @Deprecated
public final static int AAPT = 20;
- /** OS Path to the target's version of the aidl tool. */
+ /** OS Path to the target's version of the aidl tool.
+ * This is deprecated as aidl is now in the platform tools and not in the platform. */
+ @Deprecated
public final static int AIDL = 21;
/** OS Path to the target's version of the dx too.<br>
* This is deprecated as dx is now in the platform tools and not in the platform. */
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
index 713e8a8..181e7ec 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
@@ -50,6 +50,8 @@ public final class SdkConstants {
/** An SDK Project's AndroidManifest.xml file */
public static final String FN_ANDROID_MANIFEST_XML= "AndroidManifest.xml";
+ /** pre-dex jar filename. i.e. "classes.jar" */
+ public final static String FN_CLASSES_JAR = "classes.jar"; //$NON-NLS-1$
/** Dex filename inside the APK. i.e. "classes.dex" */
public final static String FN_APK_CLASSES_DEX = "classes.dex"; //$NON-NLS-1$
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/build/ApkBuilder.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/build/ApkBuilder.java
index 2148ccf..da1790e 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/build/ApkBuilder.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/build/ApkBuilder.java
@@ -47,7 +47,7 @@ import java.util.regex.Pattern;
* - Native libraries from the project or its library.
*
*/
-public final class ApkBuilder {
+public final class ApkBuilder implements IArchiveBuilder {
private final static Pattern PATTERN_NATIVELIB_EXT = Pattern.compile("^.+\\.so$",
Pattern.CASE_INSENSITIVE);
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/build/IArchiveBuilder.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/build/IArchiveBuilder.java
new file mode 100644
index 0000000..e2230e9
--- /dev/null
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/build/IArchiveBuilder.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 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.build;
+
+import java.io.File;
+
+public interface IArchiveBuilder {
+
+ /**
+ * Adds a file to the archive at a given path
+ * @param file the file to add
+ * @param archivePath the path of the file inside the APK archive.
+ * @throws ApkCreationException if an error occurred
+ * @throws SealedApkException if the APK is already sealed.
+ * @throws DuplicateFileException if a file conflicts with another already added to the APK
+ * at the same location inside the APK archive.
+ */
+ void addFile(File file, String archivePath) throws ApkCreationException,
+ SealedApkException, DuplicateFileException;
+
+}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java
index 8db9587..e2874b9 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java
@@ -603,20 +603,19 @@ public class ProjectCreator {
// Build.xml: create if not present or no <androidinit/> in it
File buildXml = new File(projectFolder, SdkConstants.FN_BUILD_XML);
boolean needsBuildXml = projectName != null || !buildXml.exists();
+
if (!needsBuildXml) {
- // Look for for a classname="com.android.ant.SetupTask" attribute
- needsBuildXml = !checkFileContainsRegexp(buildXml,
- "classname=\"com.android.ant.SetupTask\""); //$NON-NLS-1$
- }
- if (!needsBuildXml) {
- // Note that "<setup" must be followed by either a whitespace, a "/" (for the
- // XML /> closing tag) or an end-of-line. This way we know the XML tag is really this
- // one and later we will be able to use an "androidinit2" tag or such as necessary.
- needsBuildXml = !checkFileContainsRegexp(buildXml, "<setup(?:\\s|/|$)"); //$NON-NLS-1$
- }
- if (needsBuildXml) {
- if (buildXml.exists()) {
- println("File %1$s is too old and needs to be updated.", SdkConstants.FN_BUILD_XML);
+ // we are looking for version-tag: followed by either an integer or "custom".
+ if (checkFileContainsRegexp(buildXml, "version-tag:\\s*custom")) { //$NON-NLS-1$
+ println("File %1$s is custom and will not be overriden.",
+ SdkConstants.FN_BUILD_XML);
+ } else {
+ // TODO: look for the version value and update if too old.
+ if (!checkFileContainsRegexp(buildXml, "version-tag:\\s*(\\d*)")) { //$NON-NLS-1$
+ needsBuildXml = true;
+ println("File %1$s is too old and needs to be updated.",
+ SdkConstants.FN_BUILD_XML);
+ }
}
}
diff --git a/templates/build.template b/templates/build.template
index d975bdc..3dd734d 100644
--- a/templates/build.template
+++ b/templates/build.template
@@ -32,9 +32,11 @@
application and should be checked into Version Control Systems. -->
<property file="default.properties" />
-
- <!-- Required pre-setup import -->
- <import file="${sdk.dir}/tools/ant/pre_setup.xml" />
+ <!-- quick check on sdk.dir -->
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project'"
+ unless="sdk.dir"
+ />
<!-- extension targets. Uncomment the ones where you want to do custom work
@@ -45,35 +47,32 @@
<target name="-pre-compile">
</target>
- [This is typically used for code obfuscation.
- Compiled code location: ${out.classes.absolute.dir}
- If this is not done in place, override ${out.dex.input.absolute.dir}]
+ /* This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir} */
<target name="-post-compile">
</target>
-->
- <!-- Execute the Android Setup task that will setup some properties
- specific to the target, and import the build rules files.
-
- The rules file is imported from
- <SDK>/tools/ant/
- Depending on the project type it can be either:
- - main_rules.xml
- - lib_rules.xml
- - test_rules.xml
+ <!-- Import the actual build file.
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
- <setup> task.
+ <import> task.
- customize it to your needs.
- - Customize the whole script.
+ - Customize the whole content of build.xml
- copy/paste the content of the rules files (minus the top node)
- into this file, *after* the <setup> task
- - disable the import of the rules by changing the setup task
- below to <setup import="false" />.
+ into this file, replacing the <import> task.
- customize to your needs.
+
+ ***********************
+ ****** IMPORTANT ******
+ ***********************
+ In all case you should replace the following version tag value with "custom"
+ in order to avoid having your file be overridden.
-->
- <setup />
+ <!-- version-tag: 1 -->
+ <import file="${sdk.dir}/tools/ant/build.xml" />
</project>
diff --git a/testapps/basicLib/.classpath b/testapps/basicLib/.classpath
index 609aa00..765e549 100644
--- a/testapps/basicLib/.classpath
+++ b/testapps/basicLib/.classpath
@@ -3,5 +3,6 @@
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/testapps/basicLibWithJar/.classpath b/testapps/basicLibWithJar/.classpath
index cc48f32..08a60d4 100644
--- a/testapps/basicLibWithJar/.classpath
+++ b/testapps/basicLibWithJar/.classpath
@@ -3,6 +3,7 @@
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="lib" path="libs/basicJar.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/testapps/basicProject/.classpath b/testapps/basicProject/.classpath
index 609aa00..765e549 100644
--- a/testapps/basicProject/.classpath
+++ b/testapps/basicProject/.classpath
@@ -3,5 +3,6 @@
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/testapps/basicProjectWithAidl/.classpath b/testapps/basicProjectWithAidl/.classpath
index 609aa00..765e549 100644
--- a/testapps/basicProjectWithAidl/.classpath
+++ b/testapps/basicProjectWithAidl/.classpath
@@ -3,5 +3,6 @@
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/testapps/basicProjectWithJar/.classpath b/testapps/basicProjectWithJar/.classpath
index cc48f32..08a60d4 100644
--- a/testapps/basicProjectWithJar/.classpath
+++ b/testapps/basicProjectWithJar/.classpath
@@ -3,6 +3,7 @@
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="lib" path="libs/basicJar.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/testapps/basicProjectWithJava/.classpath b/testapps/basicProjectWithJava/.classpath
index 475139c..61fad04 100644
--- a/testapps/basicProjectWithJava/.classpath
+++ b/testapps/basicProjectWithJava/.classpath
@@ -3,6 +3,7 @@
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry combineaccessrules="false" kind="src" path="/basicJavaProject"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/testapps/basicProjectWithJavaFolder/.classpath b/testapps/basicProjectWithJavaFolder/.classpath
index 2fc51f4..5ae731a 100644
--- a/testapps/basicProjectWithJavaFolder/.classpath
+++ b/testapps/basicProjectWithJavaFolder/.classpath
@@ -3,6 +3,7 @@
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="lib" path="/basicJavaProject/bin"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/testapps/basicProjectWithJavaFolder/.project b/testapps/basicProjectWithJavaFolder/.project
index 5ff4505..e65e06a 100644
--- a/testapps/basicProjectWithJavaFolder/.project
+++ b/testapps/basicProjectWithJavaFolder/.project
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
- <name>basicProjectWithJava</name>
+ <name>basicProjectWithJavaFolder</name>
<comment></comment>
<projects>
</projects>
diff --git a/testapps/basicProjectWithLib/.classpath b/testapps/basicProjectWithLib/.classpath
index a4a3dc5..29e2115 100644
--- a/testapps/basicProjectWithLib/.classpath
+++ b/testapps/basicProjectWithLib/.classpath
@@ -3,8 +3,7 @@
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="lib" path="/basicLibWithJar/libs/basicJar.jar"/>
- <classpathentry kind="src" path="basicLib_src"/>
- <classpathentry kind="src" path="basicLibWithJar_src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/testapps/basicProjectWithLib/.project b/testapps/basicProjectWithLib/.project
index f6514f1..4b74bbd 100644
--- a/testapps/basicProjectWithLib/.project
+++ b/testapps/basicProjectWithLib/.project
@@ -30,16 +30,4 @@
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
- <linkedResources>
- <link>
- <name>basicLibWithJar_src</name>
- <type>2</type>
- <locationURI>_android_basicLibWithJar_aa4247fe/src</locationURI>
- </link>
- <link>
- <name>basicLib_src</name>
- <type>2</type>
- <locationURI>_android_basicLib_99a2bf77/src</locationURI>
- </link>
- </linkedResources>
</projectDescription>