summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 ebefde552..6b438bb73 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 f2fa094c7..5fa1f30dc 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 a87d0d572..000000000
--- 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 2126d3fbb..00b7fcb5f 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 a21478e87..6b2316248 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 000000000..50dcdabb8
--- /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 e15f77bf5..c0bc55d62 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 73bd92989..fcb0f3cf7 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 9d53db78d..00a5a4683 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 4ab4f583a..093ac9a82 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 e828e51bb..a0b77b4af 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 ac2e4dde4..e3ec83ccb 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 1231de300..af231855e 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 15aedaf1c..07a2a2599 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 016340117..2a988d104 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 ba77ce946..abc389f4c 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 bbef6b9b2..0b891c929 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 9ae60c00c..d83d539ec 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 4641c227a..ed4e1edbd 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 4fd3c3559..2ca42696c 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 000000000..3d0d98847
--- /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 2804bac21..428e8d965 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 f2e6485ae..6e9881f2a 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 dc543b861..44955533f 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 d57cdaff8..5b221fc19 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 0002668f6..6d476d958 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 42a1e48bc..000000000
--- 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 8404eb85c..ca4cfc992 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 eee449a1e..1c8610943 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 955fba4ca..000000000
--- 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 44c46ee98..9c369edf6 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 713e8a844..181e7ecad 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 2148ccf0b..da1790ef9 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 000000000..e2230e9ee
--- /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 8db958789..e2874b924 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 d975bdc4b..3dd734d85 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 609aa00eb..765e549ef 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 cc48f32eb..08a60d47b 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 609aa00eb..765e549ef 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 609aa00eb..765e549ef 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 cc48f32eb..08a60d47b 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 475139c71..61fad04fa 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 2fc51f47d..5ae731ada 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 5ff450522..e65e06a4b 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 a4a3dc5e1..29e2115e1 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 f6514f19d..4b74bbda0 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>